Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

My fix for the "desyncing" problem


Scorpious2k
 Share

Recommended Posts

This is a serious change. It changes more than a line or two of code, it changes **how** the client and server deal with player movement. I will give these changes over more than one post and will try to explain what I am doing and why as we go.

The first step you should take is to back-up everything. Copy the entire directory  (EVERYTHING) and save it. This way you can always go back if there are any problems.

This problem occurs because when a player moves, the client sends the server only the direction of the move. In other words, the client says "he moved that way". The server then has to calculate the player's new position. Since packets can be lost, among other things that can go wrong, it is easy for them to get out of sync.

My solution is for the client to send the X and Y coordinates with each move. The server than validates this move. If it is OK, the server does nothing - letting the client continue. If the move is not valid, the server sends a corrected position to the client.

In my next post I will give the changes to he client.

Have you made that back-up copy of your game/source directory yet??
Link to comment
Share on other sites

  • Replies 65
  • Created
  • Last Reply

Top Posters In This Topic

Client Changes

In **modClientTCP.bas Sub SendPlayerMove()**

change
```
    Call SendData("playermove" & SEP_CHAR & GetPlayerDir(MyIndex) & SEP_CHAR & Player(MyIndex).Moving & END_CHAR)
```to
```
    Call SendData("playermove" & SEP_CHAR & GetPlayerDir(MyIndex) & SEP_CHAR & Player(MyIndex).Moving & SEP_CHAR & GetPlayerX(MyIndex) & SEP_CHAR & GetPlayerY(MyIndex) & END_CHAR)
```
in **modDatabase.bas Sub SetPlayerX()**

change

```
    If X >= 0 And X <= MAX_MAPX Then
        Player(Index).X = X
    End If
```
to

```
        Player(Index).X = X
```
in **modGameLogic.bas Sub CheckMovement()**

before

```
    If Not GettingMap Then
```
insert

```
    Dim s2kX As Integer, s2kY As Integer    ' used below for temp store of X/Y
```
change the case statement

```
                Select Case GetPlayerDir(MyIndex)
                    Case DIR_UP
                        Call SendPlayerMove
                        Player(MyIndex).yOffset = PIC_Y
                        Call SetPlayerY(MyIndex, GetPlayerY(MyIndex) - 1)

                    Case DIR_DOWN
                        Call SendPlayerMove
                        Player(MyIndex).yOffset = PIC_Y * -1
                        Call SetPlayerY(MyIndex, GetPlayerY(MyIndex) + 1)

                    Case DIR_LEFT
                        Call SendPlayerMove
                        Player(MyIndex).xOffset = PIC_X
                        Call SetPlayerX(MyIndex, GetPlayerX(MyIndex) - 1)

                    Case DIR_RIGHT
                        Call SendPlayerMove
                        Player(MyIndex).xOffset = PIC_X * -1
                        Call SetPlayerX(MyIndex, GetPlayerX(MyIndex) + 1)
                End Select
```
to

```
                Select Case GetPlayerDir(MyIndex)
                    Case DIR_UP
                        Player(MyIndex).yOffset = PIC_Y
                        Call SetPlayerY(MyIndex, GetPlayerY(MyIndex) - 1)

                    Case DIR_DOWN
                        Player(MyIndex).yOffset = PIC_Y * -1
                        Call SetPlayerY(MyIndex, GetPlayerY(MyIndex) + 1)

                    Case DIR_LEFT
                        Player(MyIndex).xOffset = PIC_X
                        Call SetPlayerX(MyIndex, GetPlayerX(MyIndex) - 1)

                    Case DIR_RIGHT
                        Player(MyIndex).xOffset = PIC_X * -1
                        Call SetPlayerX(MyIndex, GetPlayerX(MyIndex) + 1)
                End Select
```
put this after that case statement

```
                Call SendPlayerMove    '090829 moved here

                s2kX = GetPlayerX(MyIndex)  '090829
                s2kY = GetPlayerY(MyIndex)  '090829
```
after that replace this line (which comes next in the sub)

```
                If Map(GetPlayerMap(MyIndex)).Tile(GetPlayerX(MyIndex), GetPlayerY(MyIndex)).Type = TILE_TYPE_WARP Then
```
with

```
                If Map(GetPlayerMap(MyIndex)).Tile(s2kX, s2kY).Type = TILE_TYPE_WARP Or s2kX < 0 Or s2kX > MAX_MAPX Or s2kY < 0 Or s2kY > MAX_MAPY Then
```
in **modTypes.bas Type PlayerRec**

```
    X As Byte
    y As Byte
```
to

```
    X As Integer
    y As Integer
```

If you have been paying close attention, we have just broken a lot of things  - not the least of which is the player data for all your current players.

Don't panic!

We will fix it in the server and even make the changes to the player data transparent to your players.

Aren't you glad you made that back-up now?

Next post: server changes.
Link to comment
Share on other sites

Server Changes

in **modGameLogic.bas Sub PlayerMove()**

change

```
Sub PlayerMove(ByVal index As Long, ByVal Dir As Long, ByVal Movement As Long)
```
to

```
Sub PlayerMove(ByVal Index As Long, ByVal Dir As Long, ByVal Movement As Long, Xpos As Integer, Ypos As Integer)
```
after

```
    Dim a As Long
```
insert

```
'  variables we will need
    Dim pmap As Integer
    Dim Xold As Integer
    Dim Yold As Integer
```
This next step is something of a rewrite of some of the code…

remove:

```
    Select Case Dir
        Case DIR_UP
            ' Check to make sure not outside of boundries
            If GetPlayerY(Index) > 0 Then
                ' Check to make sure that the tile is walkable
              If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_BLOCKED Then
                    ' Check to see if the tile is a key and if it is check if its opened
                    If (Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_KEY Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type <> TILE_TYPE_DOOR) Or ((Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type = TILE_TYPE_DOOR Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) - 1).Type = TILE_TYPE_KEY) And TempTile(GetPlayerMap(Index)).DoorOpen(GetPlayerX(Index), GetPlayerY(Index) - 1) = YES) Then
                        Call SetPlayerY(Index, GetPlayerY(Index) - 1)

                        packet = "playermove" & SEP_CHAR & Index & SEP_CHAR & GetPlayerX(Index) & SEP_CHAR & GetPlayerY(Index) & SEP_CHAR & GetPlayerDir(Index) & SEP_CHAR & Movement & END_CHAR
                        Call SendPlayerNewXY(index)
                        Call SendDataToMapBut(Index, GetPlayerMap(Index), packet)
                        Moved = YES

                    End If
                End If
            Else
                ' Check to see if we can move them to the another map
                If Map(GetPlayerMap(Index)).Up > 0 Then
                    Call PlayerWarp(Index, Map(GetPlayerMap(Index)).Up, GetPlayerX(Index), MAX_MAPY)
                    Moved = YES
                End If
            End If

        Case DIR_DOWN
            ' Check to make sure not outside of boundries
            If GetPlayerY(Index) < MAX_MAPY Then
                ' Check to make sure that the tile is walkable
                If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_BLOCKED Then
                    ' Check to see if the tile is a key and if it is check if its opened
                    If (Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_KEY Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type <> TILE_TYPE_DOOR) Or ((Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type = TILE_TYPE_DOOR Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index), GetPlayerY(Index) + 1).Type = TILE_TYPE_KEY) And TempTile(GetPlayerMap(Index)).DoorOpen(GetPlayerX(Index), GetPlayerY(Index) + 1) = YES) Then
                        Call SetPlayerY(Index, GetPlayerY(Index) + 1)

                        packet = "playermove" & SEP_CHAR & Index & SEP_CHAR & GetPlayerX(Index) & SEP_CHAR & GetPlayerY(Index) & SEP_CHAR & GetPlayerDir(Index) & SEP_CHAR & Movement & END_CHAR
                        Call SendPlayerNewXY(index)
                        Call SendDataToMapBut(Index, GetPlayerMap(Index), packet)
                        Moved = YES
                    End If
                End If
            Else
                ' Check to see if we can move them to the another map
                If Map(GetPlayerMap(Index)).Down > 0 Then
                    Call PlayerWarp(Index, Map(GetPlayerMap(Index)).Down, GetPlayerX(Index), 0)
                    Moved = YES
                End If
            End If

        Case DIR_LEFT
            ' Check to make sure not outside of boundries
            If GetPlayerX(Index) > 0 Then
                ' Check to make sure that the tile is walkable
                If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED Then
                    ' Check to see if the tile is a key and if it is check if its opened
                    If (Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_KEY Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type <> TILE_TYPE_DOOR) Or ((Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type = TILE_TYPE_DOOR Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) - 1, GetPlayerY(Index)).Type = TILE_TYPE_KEY) And TempTile(GetPlayerMap(Index)).DoorOpen(GetPlayerX(Index) - 1, GetPlayerY(Index)) = YES) Then
                        Call SetPlayerX(Index, GetPlayerX(Index) - 1)

                        packet = "playermove" & SEP_CHAR & Index & SEP_CHAR & GetPlayerX(Index) & SEP_CHAR & GetPlayerY(Index) & SEP_CHAR & GetPlayerDir(Index) & SEP_CHAR & Movement & END_CHAR
                        Call SendPlayerNewXY(index)
                        Call SendDataToMapBut(Index, GetPlayerMap(Index), packet)
                        Moved = YES
                    End If
                End If
            Else
                ' Check to see if we can move them to the another map
                If Map(GetPlayerMap(Index)).Left > 0 Then
                    Call PlayerWarp(Index, Map(GetPlayerMap(Index)).Left, MAX_MAPX, GetPlayerY(Index))
                    Moved = YES
                End If
            End If

        Case DIR_RIGHT
            ' Check to make sure not outside of boundries
            If GetPlayerX(Index) < MAX_MAPX Then
                ' Check to make sure that the tile is walkable
                If Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_BLOCKED Then
                    ' Check to see if the tile is a key and if it is check if its opened
                    If (Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_KEY Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type <> TILE_TYPE_DOOR) Or ((Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type = TILE_TYPE_DOOR Or Map(GetPlayerMap(Index)).Tile(GetPlayerX(Index) + 1, GetPlayerY(Index)).Type = TILE_TYPE_KEY) And TempTile(GetPlayerMap(Index)).DoorOpen(GetPlayerX(Index) + 1, GetPlayerY(Index)) = YES) Then
                        Call SetPlayerX(Index, GetPlayerX(Index) + 1)

                        packet = "playermove" & SEP_CHAR & Index & SEP_CHAR & GetPlayerX(Index) & SEP_CHAR & GetPlayerY(Index) & SEP_CHAR & GetPlayerDir(Index) & SEP_CHAR & Movement & END_CHAR
                        Call SendPlayerNewXY(index)
                        Call SendDataToMapBut(Index, GetPlayerMap(Index), packet)
                        Moved = YES
                    End If
                End If
            Else
                ' Check to see if we can move them to the another map
                If Map(GetPlayerMap(Index)).Right > 0 Then
                    Call PlayerWarp(Index, Map(GetPlayerMap(Index)).Right, 0, GetPlayerY(Index))
                    Moved = YES
                End If
            End If
    End Select

    If GetPlayerX(Index) < 0 Or GetPlayerY(Index) < 0 Or GetPlayerX(Index) > MAX_MAPX Or GetPlayerY(Index) > MAX_MAPY Or GetPlayerMap(Index) <= 0 Then
        Call HackingAttempt(Index, vbNullString)
        Exit Sub
    End If
```
replace it with

```
' save the current location
    Xold = GetPlayerX(Index)
    Yold = GetPlayerY(Index)
    pmap = GetPlayerMap(Index)

' validate map number

    If pmap <= 0 Or pmap > MAX_MAPS Then
        Call HackingAttempt(Index, vbNullString)
        Exit Sub
    End If

' update it to match client - this will be correct 99% of the time
    Call SetPlayerX(Index, Xpos)
    Call SetPlayerY(Index, Ypos)

' next check to see if we have gone outside of map boundries
'  if we have, need to try to warp to next map if there is one

    If Dir = DIR_UP And Ypos < 0 And Map(pmap).Up > 0 Then
        Call PlayerWarp(Index, Map(pmap).Up, Xpos, MAX_MAPY)
        Moved = YES
    ElseIf Dir = DIR_DOWN And Ypos > MAX_MAPY And Map(pmap).Down > 0 Then
        Call PlayerWarp(Index, Map(pmap).Down, Xpos, 0)
        Moved = YES
    ElseIf Dir = DIR_LEFT And Xpos < 0 And Map(pmap).Left > 0 Then
        Call PlayerWarp(Index, Map(pmap).Left, MAX_MAPX, Ypos)
        Moved = YES
    ElseIf Dir = DIR_RIGHT And Xpos > MAX_MAPX And Map(pmap).Right > 0 Then
        Call PlayerWarp(Index, Map(pmap).Right, 0, Ypos)
        Moved = YES
    End If

' restore values in case we got warped

    Xpos = GetPlayerX(Index)
    Ypos = GetPlayerY(Index)
    pmap = GetPlayerMap(Index)

' check to make sure new position is on the map

    If Xpos < 0 Or Ypos < 0 Or Xpos > MAX_MAPX Or Ypos > MAX_MAPY Then
        Call HackingAttempt(Index, vbNullString)
        Exit Sub
    End If

' Check to make sure that the tile is walkable
    If Map(pmap).Tile(Xpos, Ypos).Type <> TILE_TYPE_BLOCKED Then
' Check to see if the tile is a key and if it is check if its opened
        If (Map(pmap).Tile(Xpos, Ypos).Type <> TILE_TYPE_KEY And Map(pmap).Tile(Xpos, Ypos).Type <> TILE_TYPE_DOOR) Or ((Map(pmap).Tile(Xpos, Ypos).Type = TILE_TYPE_DOOR Or Map(pmap).Tile(Xpos, Ypos).Type = TILE_TYPE_KEY) And TempTile(pmap).DoorOpen(Xpos, Ypos) = YES) Then
            packet = "playermove" & SEP_CHAR & Index & SEP_CHAR & Xpos & SEP_CHAR & Ypos & SEP_CHAR & Dir & SEP_CHAR & Movement & END_CHAR
            Call SendDataToMapBut(Index, pmap, packet)
            Moved = YES
        End If
    End If

' at this point we have either moved or there is a problem with the new location
'  if we didn't move, we need to reset to previous locations and quit

    If Moved <> YES Then
        Call SetPlayerX(Index, Xold)
        Call SetPlayerY(Index, Yold)
        Call SendPlayerNewXY(Index)
        Exit Sub
    End If
```
in **modHandleData.bas Sub HandleData()**

change

```
            Call Packet_PlayerMove(index, Val(Parse(1)), Val(Parse(2)))
```
to
```
            Call Packet_PlayerMove(Index, Val(Parse(1)), Val(Parse(2)), Val(Parse(3)), Val(Parse(4)))
```

in **Sub Packet_PlayerMove()**

change

```
Public Sub Packet_PlayerMove(ByVal index As Long, ByVal Dir As Long, ByVal Movement As Long)
```
to

```
Public Sub Packet_PlayerMove(ByVal Index As Long, ByVal Dir As Long, ByVal Movement As Long, Xpos As Integer, Ypos As Integer)
```
change

```
Call PlayerMove(index, Dir, Movement)
```
to

```
Call PlayerMove(Index, Dir, Movement, Xpos, Ypos)
```

in  **Sub Packet_RequestNewMap()**

after

```
Public Sub Packet_RequestNewMap(ByVal Index As Long, ByVal Dir As Long)
```
insert

```
    Dim x As Integer
    Dim y As Integer
```
after

```
    If Dir < DIR_UP Or Dir > DIR_RIGHT Then
        Call HackingAttempt(Index, "Invalid Direction")
        Exit Sub
    End If
```
insert

```
    y = GetPlayerY(Index)
    x = GetPlayerX(Index)

    Select Case Dir
        Case DIR_UP
            y = y - 1
        Case DIR_DOWN
            y = y + 1
        Case DIR_LEFT
            x = x - 1
        Case DIR_RIGHT
            x = x + 1
  End Select
```
change 

```
    Call PlayerMove(Index, Dir, 1)
```
to

```
    Call PlayerMove(Index, Dir, 1, x, y)
    Call SendPlayerNewXY(Index)
```
EDIT: another problem found by Damian666, thanks again

in **Sub ClearChar()**

after

```
    Dim n As Long
```
insert

```
    ' version info
    Player(index).Char(CharNum).Vflag = 128
    Player(index).Char(CharNum).Ver = 2
    Player(index).Char(CharNum).SubVer = 8
    Player(index).Char(CharNum).Rel = 0
```
EDIT: I forgot to include this originally - thanks Damian666

in **clsCommands.cls Sub MovePlayer**

change

```
    Call PlayerMove(index, Direction, Movement)
```
to

```
    Dim x As Integer
    Dim y As Integer

    y = GetPlayerY(Index)
    x = GetPlayerX(Index)

    Select Case Direction
        Case DIR_UP
            y = y - 1
        Case DIR_DOWN
            y = y + 1
        Case DIR_LEFT
            x = x - 1
        Case DIR_RIGHT
            x = x + 1
  End Select

    Call PlayerMove(Index, Direction, Movement, x, y)
    Call SendPlayerNewXY(Index)
```

OK, so all the changes to get rid of the out-of-sync problem are almost in, but we have to change the player data and by changing it, all your player's characters won't work.

We will both change it and make sure everyone's character still works - in the next post.
Link to comment
Share on other sites

The solution for the broken player characters is to automatically fix them by recognizing they are wrong and converting them to the new format when we load them. This will make it totally transparent to players.

So, first the break… all of this is in the server

in **modTypes.bas**

change

```
Public Type PlayerRec
```
to

```
Type V000PlayerRec
```
after

```
MAXSP As Long
End Type
```
insert

```
Public Type PlayerRec
    ' General
'090829 Scorpious2k
    Vflag As Byte      ' version flag - always > 127
    Ver As Byte
    SubVer As Byte
    Rel As Byte
'090829 End
    Name As String * NAME_LENGTH
    Guild As String
    GuildAccess As Byte
    Sex As Byte
    Class As Integer
    Sprite As Long
    LEVEL As Integer
    Exp As Long
    Access As Byte
    PK As Byte

    ' Vitals
    HP As Long
    MP As Long
    SP As Long

    ' Stats
    STR As Long
    DEF As Long
    Speed As Long
    Magi As Long
    POINTS As Long

    ' Worn equipment
    ArmorSlot As Integer
    WeaponSlot As Integer
    HelmetSlot As Integer
    ShieldSlot As Integer
    LegsSlot As Integer
    RingSlot As Integer
    NecklaceSlot As Integer

    ' Inventory
    Inv(1 To MAX_INV) As PlayerInvRec
    Spell(1 To MAX_PLAYER_SPELLS) As Integer
    Bank(1 To MAX_BANK) As BankRec

    ' Position and movement
    Map As Integer
'090829    X As Byte
'090829    Y As Byte
    X As Integer
    Y As Integer
    Dir As Byte

    TargetNPC As Integer

    Head As Integer
    Body As Integer
    Leg As Integer

    PAPERDOLL As Byte

    MAXHP As Long
    MAXMP As Long
    MAXSP As Long
End Type
```
What we have done is put in a new PlayerRec and renamed the old one to V000PlayerRec. The change we actually had to make was to change the player X and Y values from byte to integer.

With the new code, it is possible to have a position of -1 to the max X/Y value +1\. In other words, one position beyond the edge of the map. This comes from the client and tells us the player is ready to warp to the next map (if possible).

Since a byte value can only contain 0-255 (no negative number) and now we **_need_** that negative number, we change them to integers.

We also added some other extra values to PlayerRec: Vflag, Ver, SubVer, and Rel. We will use these to identify whether the player data we read from the disk is old or new (in the future this can be used to identify additional changes).

The trick we use is that Vflag, which is now the first byte of the player data, which is where the character name used to be. In ASCII, characters have a values < 127\. By making Vflag > 127 we know that if Vflag < 128 it is old player data and we need to convert it. If it is > 127 it is new style and no conversion is required.

First we put in our conversion function:

In **modDatabase.bas**

before

```
Public Sub LoadPlayer(ByVal Index As Long, ByVal Name As String)
```
insert

```
Function ConvertV000(FileName As String) As PlayerRec
    Dim OldRec As V000PlayerRec
    Dim NewRec As PlayerRec
    Dim f As Long
    Dim n As Integer

    f = FreeFile
    Open FileName For Binary As #f
        Get #f, , OldRec
    Close #f

        ' General
    NewRec.Name = OldRec.Name
    NewRec.Guild = OldRec.Guild
    NewRec.GuildAccess = OldRec.GuildAccess
    NewRec.Sex = OldRec.Sex
    NewRec.Class = OldRec.Class
    NewRec.Sprite = OldRec.Sprite
    NewRec.LEVEL = OldRec.LEVEL
    NewRec.Exp = OldRec.Exp
    NewRec.Access = OldRec.Access
    NewRec.PK = OldRec.PK

    ' Vitals
    NewRec.HP = OldRec.HP
    NewRec.MP = OldRec.MP
    NewRec.SP = OldRec.SP

    ' Stats
    NewRec.STR = OldRec.STR
    NewRec.DEF = OldRec.DEF
    NewRec.Speed = OldRec.Speed
    NewRec.Magi = OldRec.Magi
    NewRec.POINTS = OldRec.POINTS

    ' Worn equipment
    NewRec.ArmorSlot = OldRec.ArmorSlot
    NewRec.WeaponSlot = OldRec.WeaponSlot
    NewRec.HelmetSlot = OldRec.HelmetSlot
    NewRec.ShieldSlot = OldRec.ShieldSlot
    NewRec.LegsSlot = OldRec.LegsSlot
    NewRec.RingSlot = OldRec.RingSlot
    NewRec.NecklaceSlot = OldRec.NecklaceSlot

    ' Inventory
    For n = 1 To MAX_INV
        NewRec.Inv(n) = OldRec.Inv(n)
    Next n
    For n = 1 To MAX_PLAYER_SPELLS
        NewRec.Spell(n) = OldRec.Spell(n)
    Next n
    For n = 1 To MAX_BANK
        NewRec.Bank(n) = OldRec.Bank(n)
    Next n

    ' Position
    NewRec.Map = OldRec.Map
    NewRec.X = OldRec.X
    NewRec.Y = OldRec.Y
    NewRec.Dir = OldRec.Dir

    NewRec.TargetNPC = OldRec.TargetNPC

    NewRec.Head = OldRec.Head
    NewRec.Body = OldRec.Body
    NewRec.Leg = OldRec.Leg

    NewRec.PAPERDOLL = OldRec.PAPERDOLL

    NewRec.MAXHP = OldRec.MAXHP
    NewRec.MAXMP = OldRec.MAXMP
    NewRec.MAXSP = OldRec.MAXSP

    ' *** add new fields ***

    ' version info

    NewRec.Vflag = 128
    NewRec.Ver = 2
    NewRec.SubVer = 8
    NewRec.Rel = 0

    ConvertV000 = NewRec

End Function
```
Next we add code to identify player data that needs to be converted when we read it from the disk and convert it.

In **Sub LoadPlayer()**

after

```
        Open FileName For Binary As #f
            Get #f, , Player(Index).Char(I)
        Close #f
```
insert

```
    If Player(index).Char(I).Vflag <> 128 Then
        Player(index).Char(I) = ConvertV000(FileName)
    End If
```
We have to start making all the new characters the new way so…

in **AddChar()**

after

```
        Player(index).Char(CharNum).Head = headc
        Player(index).Char(CharNum).Body = bodyc
        Player(index).Char(CharNum).Leg = logc
```
insert

```
    ' version info
    Player(index).Char(CharNum).Vflag = 128
    Player(index).Char(CharNum).Ver = 2
    Player(index).Char(CharNum).SubVer = 8
    Player(index).Char(CharNum).Rel = 0
```
And we are good! There is one other little thing that will give us problems.

In **Sub SetPlayerY()**

remove

```
    If Y >= 0 And Y <= MAX_MAPY Then
```
and remove

```
    End If
```

And if I haven't forgotten anything, the sync problem should now be gone.

Don't forget to make that back-up FIRST! It is always a good practice and in cases like this where we are making some pretty extensive changes it is doubly so.

If you have problems, be sure to let me know.
Link to comment
Share on other sites

@Kimimaru:

> Thanks a lot for posting this! Would it work if I just deleted all of my existing characters instead of adding in the conversion?

Yes.

Also, it would be simple enough to make an account converter as a separate utility. I have a few programs like this which I use for updating/downdating data files.
Link to comment
Share on other sites

@Kimimaru:

> Thanks a lot for posting this! Would it work if I just deleted all of my existing characters instead of adding in the conversion?

It would, but I would recommend the conversion code anyway. It would be handy if you ever need to change PlayerRec in the future. All you do is add code to check the fields and you can handle the changes in the same way.

This is one of the reasons I added it. One of the things I am working on are limited duration spells - like buffs that only stay on a player for a given time and wear off or damage-over-time spells (for example, for one minute it takes 5 HP away every 3 seconds then stops).

Knowing I have these and other ideas in the works, I wanted a way to be able to add them without any impact on players in the future.
Link to comment
Share on other sites

@Kimimaru:

> Okay, thanks! I'll try to add it in on my convenience because I don't have the time right now. I can't seem to shake off the feeling that this whole thing could've been done in fewer lines of code.

Probably. There is always a better way. When changing things at this level, I prefer to be safe and still think I managed to observe the KISS (Keep It Simple Stupid) principle. I replaced some code that might have been modified for the same effect, but I felt the new code was easier to read , easier to follow, and will be easier to maintain.
Link to comment
Share on other sites

you do knwo there is a playermove in sadscript too right?
because that one breaks this way :D

its fixable of course ^^

Damian666

Edit:
This works great, i just now walked my whole game world, from begin to end, and no hangs, just none, never XD

and normally it would hang like, 10 times, its even on my work computer, which lags like hell, and even with the lag it doesnt hang ^^

very good job man :)
Link to comment
Share on other sites

@[SB:

> Damian666 link=topic=51502.msg540941#msg540941 date=1252311290]
> you do knwo there is a playermove in sadscript too right?
> because that one breaks this way :D
>
> its fixable of course ^^
>
> Damian666
>
> Edit:
> This works great, i just now walked my whole game world, from begin to end, and no hangs, just none, never XD
>
> and normally it would hang like, 10 times, its even on my work computer, which lags like hell, and even with the lag it doesnt hang ^^
>
> very good job man :)

Thanks, and thanks for spotting my omission. I had it in my source but overlooked the sadscript one. I edited the above to include it.
Link to comment
Share on other sites

@Kimimaru:

> Yeah, I believe he made it so that it's possible for a character to have X and Y coordinates of -1 and 31 in order to prepare the player for a map transition.

@The:

> But he said something about negatives, so a byte doesn't work (I think)

You both are absolutely correct.
Link to comment
Share on other sites

@[SB:

> Damian666 link=topic=51502.msg541139#msg541139 date=1252344737]
> well… who cares XD
>
> it works great , havent got any skipping or misplacement yet with this ^^
>
> Damian666

I've been using it for over a week with no problems. :)

You mentioned lag earlier, you should notice that you can move smoothly even on a server with lag problems.
Link to comment
Share on other sites

I've added all of this in, but it's telling me that there's no **Sub SendPlayerNewXY**, since it's saying that there's a Sub or Function not defined in this statement:

```
Call SendPlayerNewXY(Index)
```
I looked through your code and didn't seem to miss anything. Are you sure you added that sub in?
Link to comment
Share on other sites

@Kimimaru:

> I've added all of this in, but it's telling me that there's no **Sub SendPlayerNewXY**, since it's saying that there's a Sub or Function not defined in this statement:
>
> ```
> Call SendPlayerNewXY(Index)
> ```
> I looked through your code and didn't seem to miss anything. Are you sure you added that sub in?

It is already in the server, in modServerTCP.bas - forth line (first sub in it):

```
Sub SendPlayerNewXY(ByVal index As Long)
    Call SendDataTo(index, "PLAYERNEWXY" & SEP_CHAR & GetPlayerX(index) & SEP_CHAR & GetPlayerY(index) & END_CHAR)
End Sub
```
I didn't add it, it was already there.
Link to comment
Share on other sites

Oh, okay. Thanks! I'm using **Eclipse Evolution 2.7**, so that's probably why it wasn't there for me. You're probably using a more updated version of Eclipse.

EDIT: Wow, this works so well! It's amazing! Thanks so much!

A great way to test this would be to go to an area with blocks, open up the mapeditor, remove the blocks, and then try to walk onto the blocked area without saving the map or exiting the map editor. It automatically moves you back to your original spot, since in the map, you're technically not allowed to move onto the blocked areas.

Thanks again!
Link to comment
Share on other sites

@Kimimaru:

> Oh, okay. Thanks! I'm using **Eclipse Evolution 2.7**, so that's probably why it wasn't there for me. You're probably using a more updated version of Eclipse.

Yes, I am using 2.8 from here: [http://rapidshare.com/files/260780159/Eclipse_2.8_.rar.html](http://rapidshare.com/files/260780159/Eclipse_2.8_.rar.html)

There are plenty of good fixes in it that would make it worth the upgrade for you.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share


×
×
  • Create New...