How to get the Correct Update Masks
Explanation/Purpose: Gaining the update masks from the client and making them work server side.
Difficulty: 4 - need a bit of knowledge to understand what I mean.
Classes Added/Modified: Clientside: Client.java; Serverside unsure.
Tested On: Should work on any base really.
Bugs/Problems: None so far.
Tutorial:
This was requested not so long ago (15th January 2009), I decided to release the masks, but then people said why not make a tutorial teaching people, so that I am going to do.
Firstly what are update masks and what do they do?
Well update masks are a way of communicating from server to client saying what a specific player or npc may require.
For example, your player appearance, wielding a dragon longsword for example, it would need updating or you would just remain the same.
So lets look into it shall we?
Firstly Client side, open the class called client and locate this:
Code:
private void playerMasks(int i, int j, Stream stream, Player player)
Now beneath it you will see some if statements, (NOTE: The order of these is crucial server side, client side we can change it around but server side it must remain the same).
Code:
if((i & 0x400) != 0)
{
player.anInt1543 = stream.readUnsignedByteS();
player.anInt1545 = stream.readUnsignedByteS();
player.anInt1544 = stream.readUnsignedByteS();
player.anInt1546 = stream.readUnsignedByteS();
player.anInt1547 = stream.readUnsignedWordBigEndianA() + loopCycle;
player.anInt1548 = stream.readUnsignedWordA() + loopCycle;
player.anInt1549 = stream.readUnsignedByteS();
player.method446();
}
Now thats one mask for the player!
How we work it out is simple, you see the i & 0x400 in the if statement, well the updateMask it requires is 0x400! Keep note of this.
Your content will not look the same as mine as I have refactored much more :p
You will see some wierd methods inside the brackets and will not know what they mean, so lets find out shall we :d!
Open the stream class and find the first method from the if statement (I do not know what it is as I refactored mine).
You will probably find something looking like this:
Code:
{
buffer[currentOffset++] = (byte)(128 - j);
}
That is the stream it requires, so now we go server side, and find an exact look a like!
I have done it for you and it is readUnsignedByteS.
So guess what our first method in our append is!
Code:
public boolean mask400updateRequired = false;
private void appendMask400Update(Stream str) {
str.writeByteS(INTEGER);
}
thats right! writeByteS!
Now for eclipse users or any ide users, this is very easy to use! all you have to do is in the Stream class highlight the whole method, and point to refactor then change method signature, and rename it to readUnsignedByteS.
Now it will change all methods that require it to that.
You can do this for every single mask!
Now actually using it server side.
Here is the full code you will need for the appendMask400Update.
Code:
public boolean mask400updateRequired = false;
private void appendMask400Update(Stream str) {
str.writeByteS(92);
str.writeByteS(92);
str.writeByteS(92);
str.writeByteS(92);
str.writeWordBigEndianA(98);
str.writeWordA(32);
str.writeByteS(59);
}
and in the appendPlayerUpdateBlock add this as the first if as it is required first client side or uh oh we say disconnection!
Code:
if (mask400updateRequired) updateMask |= 0x400;
further down underneath where it write the updateMask we add this:
Code:
if (mask400updateRequired) appendMask400Update(str);
at the top too, same reason as above, now call it and you should see something magical :d
Enjoy.
And for the people that either did/didn't want to read it, heres what you all need:
Code:
protected void appendPlayerChatText(Stream str) {
str.writeWordBigEndian(((chatTextColor & 0xFF) << 8)
+ (chatTextEffects & 0xFF));
str.writeByte(playerRights);
str.writeByteC(chatTextSize);
str.writeBytes_reverse(chatText, chatTextSize, 0);
}
private void appendAnimationRequest(Stream str) {
str.writeWordBigEndian(animationRequest);
str.writeByteC(animationWaitCycles);
}
public int focusPointX = -1, focusPointY = -1;
private void appendSetFocusDestination(Stream str) {
str.writeWordBigEndianA(focusPointX);
str.writeWordBigEndian(focusPointY);
faceFocusRequired = true;
updateRequired = true;
}
public boolean mask400updateRequired = false;
private void appendMask400Update(Stream str) {
str.writeByteS(92);
str.writeByteS(92);
str.writeByteS(92);
str.writeByteS(92);
str.writeWordBigEndianA(98);
str.writeWordA(32);
str.writeByteS(59);
}
public boolean hitUpdateRequired = false;
public int hitDiff, hitType;
private void appendHitUpdate(Stream str) {
str.writeByte(hitDiff);
str.writeByteA(hitType); // 0: red 1: blue 2: green 3: orange
str.writeByteC(playerLevel[3]);
str.writeByte(((Client) this).getActionAssistant().getLevelForXP(
playerXP[3]));
}
public boolean gfxUpdateRequired = false;
public int gfxId, gfxDelay;
private void appendGfxUpdate(Stream str) {
str.writeWordBigEndian(gfxId);
str.writeDWord(gfxDelay);
}
public boolean forceChatUpdateRequired = false;
public String chatMessage;
private void appendForceText(Stream str) {
str.writeString(chatMessage);
}
public boolean faceEntityRequired = false;
public int entityId;
public boolean isPlayer = false;
private void appendFaceEntity(Stream str) {
str.writeWordBigEndian(entityId);
}
public boolean secondHitRequired = false;
private void appendSecondHitUpdate(Stream str) {
str.writeByte(50);
str.writeByteS(1);
str.writeByteC(playerLevel[3]);
str.writeByte(((Client) this).getActionAssistant().getLevelForXP(
playerXP[3]));
}
public boolean faceFocusRequired = false;
public void appendPlayerUpdateBlock(Stream str) {
if (!updateRequired && !chatTextUpdateRequired)
return;
int updateMask = 0;
if (mask400updateRequired) updateMask |= 0x400;
if (gfxUpdateRequired) updateMask |= 0x100;
if (animationUpdateRequired) updateMask |= 8;
if (forceChatUpdateRequired) updateMask |= 4;
if (chatTextUpdateRequired) updateMask |= 0x80;
if (faceEntityRequired) updateMask |= 1;
if (appearanceUpdateRequired)updateMask |= 0x10;
if (faceFocusRequired) updateMask |= 2;
if (hitUpdateRequired) updateMask |= 0x20;
if (secondHitRequired) updateMask |= 0x200;
if (updateMask >= 0x100) {
updateMask |= 0x40;
str.writeByte(updateMask & 0xFF);
str.writeByte(updateMask >> 8);
} else {
str.writeByte(updateMask);
}
if (mask400updateRequired) appendMask400Update(str);
if (gfxUpdateRequired) appendGfxUpdate(str);
if (animationUpdateRequired) appendAnimationRequest(str);
if (forceChatUpdateRequired) appendForceText(str);
if (chatTextUpdateRequired) appendPlayerChatText(str);
if (faceEntityRequired) appendFaceEntity(str);
if (appearanceUpdateRequired) appendPlayerAppearance(str);
if (faceFocusRequired) appendSetFocusDestination(str);
if (hitUpdateRequired) appendHitUpdate(str);
if (secondHitRequired) appendSecondHitUpdate(str);
}
public void clearUpdateFlags() {
updateRequired = false;
mask400updateRequired = false;
secondHitRequired = false;
gfxUpdateRequired = false;
chatTextUpdateRequired = false;
appearanceUpdateRequired = false;
animationUpdateRequired = false;
forceChatUpdateRequired = false;
faceFocusRequired = false;
hitUpdateRequired = false;
if((!isPlayer) && entityId != -1) {
entityId = -1;
faceEntityRequired = true;
updateRequired = true;
}
}