GNet some messages are not received
BlitzMax Forums/BlitzMax Beginners Area/GNet some messages are not received
| ||
I encountered some problems with GNet again. I am sending "damage" message when player is hit, but randomly it's not received. It occurs more often when more than 2 players are connected to same server. Any ideas to fix this issue? |
| ||
Such things happened to me too. In my case the packets were not splitted correctly. So I received multiple data in one package - if you then just "read" one packageset, the rest will get deleted unread. I modified my code to use magic key data which I use to identify "merged" packages. Most time it is also used as a kind of controlling hash as it sends: - "my identifier data header" - dataLength (as controller) - data So my code just recognizes appended code (which also could be garbage) but if the code is my identifier, and the datalength afterwards corresponds to the rest of the data (datalength < length of the rest). What the problem seems to be directly: the loop gnet uses. I observed the data send and received with Wireshark and I implemented my own "data structure"-setup for Wireshark. Each packet monitored was correctly split and Wireshark was able to dissamble my data ("SetName - fields: name with Value, slot id, ... | SetGameData ... ). What this means: the implementation of gnet and the underlying lib is doing something wrong - or the loop is not doing everything correctly. The problem did not occour using 2 linux machines - but as soon as I connected virtual Windows machines and together (or mixing it with linux) the windows machines had problems using the normal gnet loop. That is why I used the base idea of gnet (objects with "automagic" data fields detection) and build an own network class using the "identifier data" on top of the packets send (and to add broadcast ability for LAN gamelobby possibility, chat messages, packet relayability ...). What really differs from gnet: Method ToPacket:TNetworkPacket() local packet:TNetworkPacket = New TNetworkPacket packet.writeInt(NET_PACKET_HEADER) packet.writeInt(state) packet.writeInt(evType) packet.writeInt(self.senderIP) packet.writeInt(self.senderPort) local packedData:byte[] = PackSlots( modified ) packet.writeBytes( packedData, len(packedData) ) return packet End Method Function FromPacket:TNetworkObject( packet:TNetworkPacket ) if not packet then return TNetworkObject.Create(0) packet.seek(0) local obj:TNetworkObject = new TNetworkObject if packet.size() >= 20 '5 ints local header:int = packet.readInt() if header <> NET_PACKET_HEADER then return null obj.state = packet.readInt() obj.evType = packet.readInt() obj.senderIP= packet.readInt() obj.senderPort= packet.readInt() if obj.senderIP = 0 then obj.senderIP = packet.ip if obj.senderPort = 0 then obj.senderPort = packet.port if packet.Size() > 20 local rawdata:byte ptr = MemAlloc( packet.Size()-20 ) local packdata:byte[] = New Byte[ packet.Size()-20 ] packet.readbytes(rawdata, packet.Size()-20 ) MemCopy packdata,rawdata, packet.Size()-20 MemFree rawdata obj.UnpackSlots(packdata) packdata = null else For Local i:int = 0 Until 32 obj.slots[i] = New TNetworkObjectSlot Next endif endif return obj End Function So most "work" is to check wether the packet has the minimum size (data header and so on) and if the header is correct. Maybe you can fiddle in something similar. bye Ron |
| ||
Thanks Derron. Good to see someone with knowledge in GNet. :) Before i start doing any changes to my code, i need to know am i even right way with my current code. When player is hit. Send damage message: ' IF PLAYER HIT If GetEntityKey(mpick.entity, "player") Then enemyHit = True Print "Hit! "+GetEntityKey(mpick.entity, "player") Local localDmg:TGNetObject = CreateGNetMessage:TGNetObject( host ) SetGNetString localDmg, SLOT_TYPE, "damage" SetGNetString localDmg, SLOT_NICKNAME, GetEntityKey(mpick.entity, "player") SetGNetString localDmg, SLOT_NICKNAME2, nickname SetGNetFloat localDmg, SLOT_DMG, weapDamage SendGNetMessage(localDmg, remoteObj) EndIf Receive damage message: Case "damage" If nickName = GetGNetString(msgs, SLOT_NICKNAME) And health>0 Then health=health - GetGNetFloat(msgs, SLOT_DMG) If health <= 0 And deathMessageSent = False Then deathMessageSent = True Local localKill:TGNetObject = CreateGNetMessage:TGNetObject( host ) SetGNetString localKill, SLOT_TYPE, "kill" SetGNetString localKill, SLOT_NICKNAME, GetGNetString(msgs, SLOT_NICKNAME) SetGNetString localKill, SLOT_NICKNAME2, GetGNetString(msgs, SLOT_NICKNAME2) SetGNetString localKill, SLOT_TEAM, team$ SendGNetMessage (localKill, remoteObj) EndIf Print "HEALTH: "+health+" (dmg: "+GetGNetFloat(msgs, SLOT_DMG)+")" hurt=True EndIf And what is best way to fix this in this case. My game has lots of code and i am searching easy way to fix this issue. |
| ||
Like said... just BEFORE "select ... case EVENTTYPE/damage/..." you have to decide wether it is a valid data packet - and you have to repeat that whole thing until no more data is present (if data somehow from two packages somehow got merged). Suggestion/Hint: Case "damage" If nickName = GetGNetString(msgs, SLOT_NICKNAME) And health>0 Then health=health - GetGNetFloat(msgs, SLOT_DMG) using IF ... in multiline code is "if [newline] ... [newline] endif", singleline "if ... then ..." so: Case "damage" If nickName = GetGNetString(msgs, SLOT_NICKNAME) And health>0 Then health=health - GetGNetFloat(msgs, SLOT_DMG) hint2: health=health - GetGNetFloat(msgs, SLOT_DMG) 'is the same as health:- GetGNetFloat(msgs, SLOT_DMG) SetGNetString localKill, SLOT_TYPE, "kill" 'is the same as - but brackets are more error prune concerning "reading" SetGNetString( localKill, SLOT_TYPE, "kill" ) As I don't use gnet but my own wrapper I cannot say if the way you use it is correct (but it seems valid). Instead of the procedural style "GetGNetXXX(obj, KEY)" I have a networkobject which can be read with obj.GetInt(numberORkey). That is nearly the same - but my objects have helperfunctions too - and thingies like "defaultValue" (obj.GetInt(playerID,-1) which helps writing less and more readable code. Using "events" also adds the possiblity to produce an base networking class which can be used by specific game code. ps: game logic: even if there is a package coming with "damage" it is not given that one was really hurt... So check for dmgValue (and clamp them to possible values - so no -15677575675 values of wrong read data are possible). In short: validate your data (does that player/team exist? -> return/break if not...), don't set indicator flags if data is corrupt or in other ways not correct (if dmgOk then hurt = true). Same is valid for processing data (if dmgOK and health <= 0... killMsg). bye Ron |
| ||
Yes... it definetly looks like there is some packets missing. It registers hit to target player, but damage is missing so no damage is dealed. Thanks. I'll try to fix this. |
| ||
Woah.. this is weird. Target nickname packet goes for different player and damage to another. :o Edit: Nevermind i found problem. Last edited 2012 |
| ||
Another question. What happens to GNetObject when client reads it? Does it disappear or what? And as i said earlier that only damage variable is missing. I was wrong. Client never gets "damage" message. |
| ||
basic tutorial for gnet: http://www.blitzbasic.com/Community/posts.php?topic=51171 Another question. What happens to GNetObject when client reads it? Does it disappear or what? GNetSync seems to build up a "modified since last check"-list GnetObjects (host:TGnetHost) then returns a list containing modified objects for the given host So if you do not "sync" for a long time, the list will get longer, if you sync twice, the GnetObjects will be empty (thats why you do a "foreach" with the objects - to process all of the "new ones"). Please take care of only trying to modify "local" objects. Gnet uses a methodology which is kind of: Each player stores its local object (each ".exe" has an own "global player:TPlayer" and "remotePlayers:TList 'list with players"). Each client can only modify "player", modifications at "remotePlayers" are not possible. Modifications at them are done automatically during GnetSync. What can be done? You receive the modified objects - you can switch the eventTypes and print debug messages, do other things... but you cannot (as not needed) adjust data of the remote objects. bye Ron |
| ||
Looks like that GNet tutorial page isn't loading correctly. I loads the page forever. Tried to navigate to that tutorial by myself, but not working. Edit: Only some lag. Now i can read it. Last edited 2012 |
| ||
The forum is under stress at the moment (all pages have hickups) ... it happens so since weeks. bye Ron |
| ||
Yeah... this is very confusing. You said i need to repeat receive of data until i have everything, how can i even know if client has got that packet? |
| ||
In your case you won't be able to "repeat receive" as it is done using GnetSync So it may not be needed or has to get circumvented in a way not known to me. bye Ron |
| ||
I solved this, but i don't know if this is good way to do it... My solution is that if damage package is not meant to receiver, i send it forward. Seems like it makes little bit lag, but not very much. I don't know what happens if there is like 10 players in same server. I still wait for better solution, but i keep it this way for now. |
| ||
the normal way should be: - store local player object - store list of remote player objects on damage done to you: modify your local player object (SetGNetInt( localPlayerNetObj, DMG_RECEIVED_AKA_OBJ_SLOT, dmgValue) ) during gnetSync: your modifications are send to others receiving modified object data: if event is "damage" - handle it (eg. draw "remote player got hit") To check for cheaters you could store the damage locally (but do not modify the remote player object by yourself) Hmpf... I just wrote an example and recognized: it is similar to BlitzMax/samples/mak/gnetdemo.bmx bye Ron |