Welcome back.
Now I'm here in gameplay effect types dot CP taking a look at the gameplay effect context net serialize
function which takes three inputs now be out.
Success is probably the most self-explanatory.
It returns true if serialization was a success.
Let's keep working from right to left and take a look at package map.
The U.
Package Map class.
If we right click and go to declaration or usages, we see that it's a U object and it says maps, objects
and names to and from indices for network communication.
So this is just a tool used to help map objects to indices.
Why?
Because when serializing all the objects, all the variables of a struct are converted into strings
of zeros and ones.
And you can think of this as a bit array, an array of bits, zeros and ones and when you serialize
a whole bunch of things next to each other, it helps to know what's the index for when one object starts
and another begins.
So u package map is used for that.
Now the leftmost or first input parameter is the archive called R.
Now let's take a look at the archive.
We'll go to declaration or usages and the comment says base class for archives that can be used for
loading, saving and garbage collecting in a byte order neutral way.
Byte order Neutral is kind of networking lingo for the way that the bytes are handled.
In a word, a word is a unit of data that contains bytes and byte order.
Neutral is sometimes referred to as big endian endian with an E, not with an eye.
And it's just a way that bytes are ordered in each word or each fundamental unit of data for a processor.
Now it doesn't really matter.
We don't have to know too deeply how that works.
We just have to know that F archive is capable of storing serialized data and it also has the ability
to serialize things.
Notice it has the operator left shift overloaded for lots of types.
When you see this operator with an operator here and parentheses, this is an operator overload.
And this right here says that we can use the left shift operator with an F archive on the left hand
side and an F text on the right hand side.
And look, the comment says serializes an F text value from or into an archive.
Now there are lots of these for lots of different types, so the F archive is capable of serializing
lots of different types.
But the key words I want you to see that I don't want you to overlook are from or into.
Now that's kind of interesting from or into seems to imply that we can take an F text and use the left
shift operator and serialize it into the archive.
Or we can take that f text and the archive can take the serialized data and unserialize it and store
it in that F text.
So the left shift operator yes, it looks like it's pointed to the left, but think of it as both ways.
It works to and from the archive.
Let's talk about this.
So we know that there's this archive struct and its type is F archive and we know that it's good for
saving loading and storing data.
Now it stores this data serialized as a series of bits and it overloads this left shift operator for
lots of different types and we know that it works both ways depending on the context, if we're saving
or loading.
And that's exactly how it works.
If we have an F archive and we're using the left shift operator and on the right hand side we have some
Boolean variable, for example, Well, if we're saving, then that left shift operator is going to
take that boolean value and serialize it and store it in the archive.
However, on the other hand, if the context is different, if we're in the process of loading, then
we're going in the reverse direction.
That series of bits is deserialized and then stored in that boolean that exists on the right hand side
of the left shift.
Operator.
So just be aware of the versatility of the F archives overloads for this operator.
So now that we know about that, let's go back to gameplay effect types.
And I'd actually like to go back to the CPP file gameplay effect types dot CPP because we need to understand
what's going on here.
We know about the input parameters and what they do for us, but now let's talk about the logistics
of how this happens.
Now the first line here is a Uint8 declaration, a local variable called rep bits, and this unsigned
eight bit integer is set to zero.
Now we have an if statement taking the R this F archive and checking to see if it's saving.
So the archive seems to know if we're saving or loading data and if we're saving it performs some kind
of check, some validation, checking the member variables.
It's checking be replicate, instigator and instigator is valid.
What it's doing is trying to decide should we serialize that instigator here?
If the answer is yes, then that means we want that instigator in our gameplay effect context struct
to be replicated whenever this struct gets replicated.
Now, if instigator is not valid, well then why bother serializing it?
Right?
So that's why we're checking.
But what is this business here?
Rep bits pipe symbol equal sign one left shift zero.
How do you even read this?
Well, it's read as rep bits bitwise or equals one left shift zero.
In order to understand this line, we have to kind of break it down piece by piece.
So let's analyze this a little closer.
We know that Net Serialize is creating a local variable called bits and rep bits is an unsigned eight
bit integer.
What that means is in binary this is all zeros with eight binary digits.
Eight bits a u and eight is one byte, which is eight bits in size and we're initializing it to zero.
So all eight bits are zero in this variable.
Now the line that we're confused about is this one rep bits bitwise or equals one left shift zero.
Now bitwise or equals is just like plus equals or times equals.
It's equivalent to taking rep bits and setting it equal to itself.
Bitwise or one left shift zero.
So these two lines are equivalent.
Now this here is the bitwise or it's half of the or symbol that we're all familiar with.
And this is the left shift or shift left.
Operator So what exactly are these operators doing?
Well, I'd like to take a look at the bitwise or just by itself.
Let's say we have a variable called oh, it's a Uint eight and let's say that its value is zero, which
means in binary it's eight bits, eight digits, all of which are zero.
Now let's say we have another integer called one.
This is also a uint eight and in binary this is what it looks like eight digits, all zeros except for
the rightmost digit, which is a one.
Now let's say we'd like to perform a bitwise operation on it.
The bitwise or.
So how does the bitwise or work?
Well, the result is an unsigned eight bit integer, eight digits long, and each digit is the result
of an or of one bit in one of the integers and one bit in the other integer.
So let's take the leftmost digit.
In each one we perform an Or.
Now how does an or work with Booleans If both the left and right hand side are false, then the result
is false.
If either one of them is true, then the result is true.
That's how ors work.
Well, they work the same with ones and zeros as well.
If both digits are zeros, then the result is a zero.
That means for the next digit over.
And each one we compare them, they're both zero and that means the result is zero.
The same goes for the rest of these.
We have all zeros.
So for the rest of those digits in the result, we get zero.
The only place where they're different is this very rightmost digit In each of these integers in O it's
a zero and in one it's a one.
Now, zero bitwise or one is going to result in one If either one of them are a one, the result is
a one.
So if they're both zero, the result is a zero.
So the result of a bitwise or of O and one is this.
All zeros with a one at the end.
That's just the number one.
Now that was a pretty easy example.
Let's try another one.
Let's say that O is this number.
Here we have a one in a digit that's two away from the end at the left there.
And we have one which is this number.
Now the bitwise or results in a you int eight and we check bitwise each digit performing the Or.
Now these pairs of digits are all zeros, so that means there's zero in the result.
And these pairs of digits all have at least a 1 in 1 or the other of the two variables.
And that means that the results for these digits are all ones.
So the result of the bitwise or of these two numbers is this number.
So now that we know about the bitwise or the thing we need to talk about next is the shift left.
Operator.
Now, when used like this with an integer to the left and an integer to the right, we need to think
about the result in terms of bits.
Now, in most modern compilers, integers are 32 bits.
But we're going to treat these like they're eight bit integers, just for simplicity's sake, for now.
So the left hand side of the left shift operator is one.
If it were an unsigned eight bit integer, it would be this value.
Now what this expression one left shift zero means is to shift all the digits to the left by zero.
In other words, don't shift them at all.
So that's pretty easy.
Let's take another example.
One left shift one.
Now, again, assuming that these integers are eight bit, we would take the left side of the shift
left operator, which is this number and we shift left by one.
Now that means we shift all the digits to the left by one.
And so this one here is going to end up one digit over.
And any digits coming in from the right hand side will be zero.
So the one at the very end will be replaced by a zero and the digit next to it will now be a one.
Now, what about this digit all the way over here to the left?
It's getting pushed out.
We completely lose that.
So if it was a one, it would be gone.
And whatever was to the right of that endmost digit is now going to replace it.
So one left shift, one is going to result in this value all zeros, but that one has shifted over.
Now, all those other zeros to the left of the one have shifted over as well.
So if they were ones instead, those would also be shifted over.
So keep that in mind.
Let's do another simple example.
One left shift two.
Here's one.
And we need to shift all the digits to the left by two.
So the one all the way there at the right is going to shift over once and twice.
And those two zeros all the way at the end are going to be pushed out and they'll be lost.
So the result of this is this value here.
That is how left shift works.
And just to drive the point home, let's try one left shift three.
Here's one.
And we have to shift to the left by three, which means the one will travel 1 to 3 digits over these
three digits at the end will be lost and the result will be this number.
Now rep bits is a UNT eight, but it doesn't have to be.
Let's say for example sake that we create one in our net serialize.
That's a UN 32.
That would be 32 bits.
And if we initialize it to zero, it would be all zeros.
Here's what the bits would look like.
Now if we take rep bits and we use say this operation on it, rep bits bitwise or equals one left shift
zero, that would be equivalent to taking rep bits and setting it equal to itself.
Bitwise or the expression one left shift zero.
Let's see what result we would get from this.
Here is one left shift zero.
As we've seen, one left shift zero is going to take the value one and shift all the digits to the left
by zero.
In other words, it's just one.
So since we know one left shift zero, we can take rep bits which starts off at zero, do a bitwise
or with one left shift zero, which we know is one and the result is going to be rep bits.
Here's all the zeros bitwise or and here's one left shift zero, which is just one.
What do we get when we take the bitwise or of these two numbers?
Well, we get one as all the digits are zero except for that one at the very right and the bitwise or
between 0 and 1 gives us one.
So the result is one.
Okay, simple example.
But the thing is, rep bits has changed.
It started off with all zeros and now it has a one right there at the end.
So this expression that we've done this rep bits bitwise or equals one left shift zero has changed rep
bits in a way, and we refer to that change as flipping that bit at the end there.
Now if we count bits from right to left and the very first digit there, we consider to be the zeroth
digit, then we say we flipped the zeroth bit.
Now after we've flipped the zeroth bit on rep bits, we know that its value is now equal to this.
All zeros with a one at the end.
Now let's say we take rep bits and do this again only we use one left shift one this time.
How does this work?
Well, we know that one left shift one is this value.
It's the value one.
But all the digits have been shifted to the left by one digit.
So it's now no longer just one.
It's this number.
So if we were to take rep bits and do the bitwise or with one left shift one, we would have rep bits,
which is one.
Or and then one left shift, one which is this value.
Now, what's the result of the bitwise or between these two?
Well, it would be this.
All the digits are zero, except one of the numbers has a one at the end and the other has a one.
Right.
They're one away from the end.
So the result is a bunch of zeros with two ones at the end.
In other words, we've flipped the first bit if we're counting from right to left starting at zero.
So one left shift, one is going to flip the first bit and notice that we kept the bit all the way at
the end there.
Rep bits started off with that bit set to one.
All the others were zero, but this line has now flipped that second bit, so they're both one.
Now let's say that we take our 32 bit and rep bits and we've already flipped the two end most bits on
it.
And let's say that we take rep bits and we do this operation on it, rep bits, bitwise or equals one
left shift two What does this do?
Well, what is one left shift?
Two it's this number, it's the number one with the one digit shifted over by two.
So it's right there, two away from the end.
And if we want to calculate rep bits or one left shift two, then we would take rep bits which is now
this number or.
And then we would take one left shift to which is this number.
Now, what's the result of the bitwise or.
Well, here it is.
We took rep bits which had two ones at the end and we flipped the second bit.
Now it has three ones at the end.
So we're starting to see a pattern here.
Now let's go back into the code and find out just what this means for us.
We're only doing this if the archive is in saving mode.
So look at what each of these if statements is checking first.
We saw that it's checking if the instigator is valid and if we should replicate the instigator.
If B replicate instigator is true.
If those are both true, then we flip that zeroth bit and rep bits has all zeros, but it now has actually
a one at the rightmost digit.
This is a very compact way of storing a bunch of boolean values.
Basically we're designating the instigator to be represented by the rightmost digit in this rep bits
bit array.
The effect causer just happens to be represented by the next one because we're checking to see if it's
valid and if we should replicate it.
And if so, we flip that next bit.
On rep bits we then check the ability variable.
If that's valid, we flip the next bit.
So assuming these are all valid then rep bits is changing by having its zeros replaced by ones in each
subsequent bit going from right to left.
And if one of these fails, say source object is not valid, for example, then we will not flip that
bit and then we'll continue.
If actor's num is not greater than zero, we don't flip that fourth bit, right?
So by the end of this, this eight bit integer has zeros and ones that represent whether or not we should
replicate or whether or not we should serialize these variables.
Now after that are calls serialized bits.
And it takes rep bits passes that in it actually passes it in by address this is the address of operator
and then it receives the length in bits.
Now let's count these.
We have one, two, three, four, five, six, seven times that we've flipped bits.
So serialized bits wants to know if we've used all those bits or not.
We didn't.
We only flipped seven of them.
That last bit wasn't used.
So we're telling serialized bits we only care about the first seven and serialized bits is going to
do some really cryptic looking serialization by taking those seven bits, however many we pass in here
and serializing those into the archive.
So it's going to know it's going to remember in the archive.
The archive is just a string of bits and it's going to remember that these seven bits are representative
of whether or not we should serialize these variables.
Now after that, look at this.
We're checking rep bits.
Now we're doing something else with rep bits that looks probably equally confusing.
Now keep in mind this first set of bit flipping steps here is done inside the if check if R is saving.
But after that this stuff is in no such if check.
So all of this only happens if saving.
But all of this happens regardless of saving or not.
And remember, this operator works both ways.
If we're saving, we're going from right to left and we're serializing what's on the right into the
archive.
But if we're not saving, then it goes from left to right.
Whatever is serialized becomes serialized and stuck right into the variable on the right.
Now the question is what are these conditionals doing right?
This might make you sort of scratch your head a bit.
So let's talk about this part.
Now we have rep bits and then this is the bitwise and similar to the bitwise or only it's the and version.
And then we have some left shifting going on.
This first one is rep bits, bitwise and one left shift zero.
So let's analyze this one.
So in these if statements we have rep bits.
Now, in this example, let's just say it's a UN 32.
We know it's a UN eight, but it doesn't really matter.
Continuing with our example though, let's say that rep bits is this number right?
We've already flipped the first three bits.
Now in our if statement, we're checking if rep bits bitwise and one left shift zero.
So let's look at this one piece at a time.
One left shift zero.
We know that that's this number.
So what is this?
Rep bits.
Bitwise and one left shift zero.
Well, rep bits is this number.
Bitwise And this number, which is one left shift zero.
What does this produce?
Well, with a bitwise.
And it's just like a bitwise or it's happened bitwise bit by bit comparing one digit on one variable
with one digit on the other.
And if they're both zero, the result is a zero.
If only one of them is a zero, the result is a zero.
Just like an and.
True and false returns.
False.
The only way we'd get a one is if both digits are ones.
That's only that rightmost digit there.
So rep bits and one left shift zero is this value.
So now this is all inside of an if statement, isn't it?
Let's take a look at this again.
Here it is.
So we know that in our hypothetical situation, if this all evaluated to a bunch of zeros with a one
at the end, well, that's just a one and it's a non-zero value.
And if statements consider any non-zero value to be true, it considers only zero values to be false.
So in our hypothetical situation, we would make it inside this if statement.
Now, what's the significance of that?
The significance is that the result of this operation gives us true because that first digit, which
we called the zeroth digit, is one.
We interpret that as the first variable represented by that digit has been serialized or should be serialized.
Both are the case In the case where we're saving it should be serialized.
We're going right to left in the case where we're loading, it has been serialized and needs to go from
left to right.
So basically, if we're checking rep bits versus one left shift zero with a bitwise and we know that
one left shift zero is all zeros, but a one on the zeroth digit, the rightmost digit if rep bits has
a one there, we'll make it inside this.
If rep bits has a zero there we will not.
In other words, if we flipped that bit up here because we know that the instigator should be replicated,
then we'll make it inside here.
If we never flipped that bit because instigator was not valid, for example, then we don't need to
get inside here and archive the instigator or unarchive.
It makes sense.
And the same goes for the rest of these.
We're checking rep bits against one left shift one that's all zeros except a one just next to the end
digit.
There's a zero at the end and a one next to it.
Right.
If rep bits has a one there, that means we flipped it up here and we did that because we wanted to
replicate effect calls or we wanted to serialize it because it was valid.
Right?
If it was valid, that bit will be a one.
We'll make it inside here and we'll get to serialize or unserialize.
So you catch the drift and the archive overloads the left shift operator for lots of types, but not
all of them.
Notice that for the array here, we can't just use the array.
With the left shift, we have to use other means.
There's this function SafeNet serialize array, which will serialize the array.
We also see that the hit result had to be handled a little differently In the case that the archive
is loading, there's a check here to see if the hit result is not valid.
If it's not, then a new hit result shared pointer is created and stored in the hit result shared pointer
member variable and then the hit result itself has a net serialized function designed to serialize hit
results.
So that's how you serialize hit results.
SafeNet Serialize array underscore default is a way to serialize arrays.
So as you can see, there are a couple of exceptions to this left shift business.
And way down here at the bottom, notice there's a check to see if the archive is loading and if so,
we call add instigator.
Well, we know what this does and the comment is telling us why it says just to initialize instigator
ability system component, because we know that that gets set along with the instigator and the effect
causer as well.
So this function does the courtesy of calling add instigator and then it just sets be out success equal
to true and it returns true.
So all of that is just net serialize for the gameplay effect context.
Pretty crazy, right?
And we need to do this in our custom effect context.
Next.
And not only do we need to take care of all these, but we also want to handle serializing our new to
Boolean variables.
And we're going to do it just like it's done here in net serialize.
So what do you think?
Are you up to a challenge?
Would you like to do this on your own or would you like to follow along with me?
You're going to get that choice in the next video, and I'll see you soon.