Josh Posted January 10, 2010 Share Posted January 10, 2010 Take a look at this. Instead of a host/peer design, this is a more intuitive server/client design. If you are hosting a game, you create a server. If you want to play in the game yourself, create a client. Here, I created two clients. You could create a client for each bot and run them through the network, or just one client for yourself. You'd probably want to always run the game through the network, even if it is single-player, so that you have a uniform design for single and multi player modes. The messages are turned into a form with an ID and a packet, which is a stream of any length. There are built-in message IDs. Some are handled entirely silently. For example, when the server receives a chat message, it just broadcasts the data to all clients, without the user having to do anything. Synced entities would behave this way as well. The user's interaction with the system comes in the form of a callback. The callback can be used to do stuff when messages are received. In the example below, the client callback is used to print the ping and to print any chat messages that are sent. SuperStrict Private Function enet_host_port:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[1] Local port:Int=(Short Ptr peer)[4] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return port EndFunction Function enet_host_ip:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[1] Local port:Int=(Short Ptr peer)[4] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return ip EndFunction Function enet_peer_port:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[3] Local port:Int=(Short Ptr peer)[8] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return port EndFunction Function enet_peer_ip:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[3] Local port:Int=(Short Ptr peer)[8] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return ip EndFunction Type TNetworkNode Field port:Int Field ip:Int Field enethost:Byte Ptr Field enetpeer:Byte Ptr Method Delete() If enethost enet_host_destroy(enethost) enethost=Null EndIf EndMethod Method Update(callback(client:TClient,id:Int,packet:TPacket)=Null) Local ip:Int,port:Int,client:TClient,ev:ENetEvent=New ENetEvent,id:Byte,packet:TPacket If Not Self.enethost RuntimeError "Can't update a remote server." Repeat If enet_host_service(Self.enethost,ev,0) Select ev.event Case ENET_EVENT_TYPE_CONNECT id=NETWORK_CONNECT Case ENET_EVENT_TYPE_CONNECT id=NETWORK_DISCONNECT Case ENET_EVENT_TYPE_RECEIVE Local size:Int=enet_packet_size(ev.packet) Local data:Byte[size] MemCopy(Varptr id,enet_packet_data(ev.packet),1) If size>1 packet=New TPacket packet._bank.resize(size-1) MemCopy(packet._bank.buf(),enet_packet_data(ev.packet)+1,size-1) EndIf Default Continue EndSelect EvaluateEvent(id,packet,ev.peer) Else Exit EndIf Forever EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Abstract EndType Public Const NETWORK_CONNECT:Int=1 Const NETWORK_DISCONNECT:Int=2 Const NETWORK_PINGREQUEST:Int=3 Const NETWORK_PINGRESPONSE:Int=4 Const NETWORK_JOINREQUEST:Int=5 Const NETWORK_JOINREPLY:Int=6 Const NETWORK_CHAT:Int=7 Type TServer Extends TNetworkNode Const maxplayers:Int=64 Field clients:TList=New TList Field callback(server:TServer,id:Int,packet:TPacket) Method Delete() If enetpeer enetpeer=Null EndIf If enethost enet_host_destroy(enethost) enethost=Null EndIf EndMethod Function Create:TServer(ip:Int=0,port:Int=7777) Local server:TServer=New TServer,addr:Byte Ptr If ip server.ip=ip Else server.ip=ENET_HOST_ANY EndIf server.port=port If server.port<>0 Or server.ip<>ENET_HOST_ANY addr=enet_address_create(server.ip,server.port) EndIf server.enethost=enet_host_create(addr,maxplayers,0,0) If addr enet_address_destroy(addr) EndIf If Not server.enethost Return Null EndIf Return server EndFunction Method FindClient:TClient(ip:Int,port:Int) Local client:TClient For client=EachIn clients If client.ip=ip And client.port=port Return client Next EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Local client:TClient Local issilent:Int=False client=FindClient(enet_peer_ip(enetpeer),enet_peer_port(enetpeer)) Select id Case NETWORK_CONNECT If client Disconnect(client,True) client=New TClient client.enetpeer=enetpeer client.ip=enet_peer_ip(enetpeer) client.port=enet_peer_port(enetpeer) client.link=clients.AddLast(client) Case NETWORK_PINGREQUEST Send(client,NETWORK_PINGRESPONSE,packet) issilent=True Case NETWORK_CHAT Broadcast(id,packet) issilent=True EndSelect If Not issilent If callback callback(Self,id,packet) EndIf EndIf EndMethod Method Send:Int(client:TClient,id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local enetpacket:Byte Ptr Local result:Int If Not client.enetpeer RuntimeError "Can't send to local client." Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) result=(enet_peer_send(client.enetpeer,channel,enetpacket)=0) Return result EndMethod Method Broadcast(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local enetpacket:Byte Ptr Local result:Int Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) enet_host_broadcast(Self.enethost,channel,enetpacket) EndMethod Method Disconnect(client:TClient,force:Int=False) If client.enetpeer If force enet_peer_reset(client.enetpeer) Else enet_peer_disconnect(client.enetpeer) EndIf client.link.remove() EndIf EndMethod EndType Type TClient Extends TNetworkNode Const maxplayers:Int=64 Field link:TLink Field server:TServer Field connected:Int Field callback(client:TClient,id:Int,packet:TPacket) Function Create:TClient(ip:Int=0,port:Int=7776) Local client:TClient=New TClient Local addr:Byte Ptr If ip client.ip=ip Else client.ip=ENET_HOST_ANY EndIf client.port=port If client.port<>0 Or client.ip<>ENET_HOST_ANY addr=enet_address_create(client.ip,client.port) client.enethost=enet_host_create(addr,maxplayers,0,0) If addr enet_address_destroy(addr) If Not client.enethost Return Null EndIf Return client EndFunction Method Disconnect(force:Int=False) If server If force enet_peer_reset(server.enetpeer) Else enet_peer_disconnect(server.enetpeer) EndIf server=Null EndIf EndMethod Method Connect:TServer(ip:Int,port:Int) Local addr:Byte Ptr,server:TServer If Not Self.enethost RuntimeError "Remote client cannot connect to server." If server Disconnect() server=New TServer server.ip=ip server.port=port addr=enet_address_create(server.ip,server.port) server.enetpeer=enet_host_connect(Self.enethost,addr,1) enet_address_destroy(addr) If server.enetpeer=Null Return Null Self.server=server Return server EndMethod Method Send:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local enetpacket:Byte Ptr Local result:Int If Not connected Return 0 If Not server RuntimeError "Client is not connected." Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) result=(enet_peer_send(server.enetpeer,channel,enetpacket)=0) Return result EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Local issilent:Int=False Select id Case NETWORK_CONNECT Self.connected=True Case NETWORK_PINGRESPONSE Case NETWORK_CHAT EndSelect If Not issilent If callback callback(Self,id,packet) EndIf EndIf EndMethod Method Ping:Int() Local packet:TPacket=New TPacket packet.WriteInt(MilliSecs()) Return Send(NETWORK_PINGREQUEST,packet) EndMethod Method Say:Int(text:String) Local packet:TPacket=New TPacket packet.WriteLine(text) Return Send(NETWORK_CHAT,packet) EndMethod EndType Type TPacket Extends TBankStream Method New() _bank=New TBank EndMethod EndType Function CreatePacket:TPacket() Local packet:TPacket=New TPacket Return packet EndFunction '---------------------------------------------------------------------------------------------------- Local server:TServer Local client1:TClient Local client2:TClient Local ip:Int=HostIp("127.0.0.1") server=TServer.Create(ip,7777) client1=TClient.Create(ip,7776) client2=TClient.Create(ip,7779) client1.connect(server.ip,server.port) client2.connect(server.ip,server.port) client1.callback=ClientCallBack client2.callback=ClientCallBack server.callback=ServerCallBack Repeat server.Update() client1.Update() client2.Update() Delay 1 If Rand(100)=1 client1.Ping() client2.Say("Hello!") EndIf Forever Function ServerCallBack(server:TServer,id:Int,packet:TPacket) Select id Case NETWORK_CONNECT Print "New client connected." EndSelect EndFunction Function ClientCallBack(client:TClient,id:Int,packet:TPacket) Select id Case NETWORK_CONNECT Print "Connected to server." Case NETWORK_CHAT Print "Says: "+packet.ReadLine() Case NETWORK_PINGRESPONSE Print "Ping = "+(MilliSecs()-packet.ReadInt()) EndSelect EndFunction Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Josh Posted January 10, 2010 Author Share Posted January 10, 2010 Here's a more advanced version. This is actually pretty fun once you get into it, because you can design your own protocols as you see fit. This handles chat, whisper, kicking, banning, recurring names, and name changes: SuperStrict Private Const ENET_PACKET_FLAG_UNSEQUENCED:Int=2 Function enet_host_port:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[1] Local port:Int=(Short Ptr peer)[4] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return port EndFunction Function enet_host_ip:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[1] Local port:Int=(Short Ptr peer)[4] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return ip EndFunction Function enet_peer_port:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[3] Local port:Int=(Short Ptr peer)[8] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return port EndFunction Function enet_peer_ip:Int( peer:Byte Ptr ) Local ip:Int=(Int Ptr peer)[3] Local port:Int=(Short Ptr peer)[8] ?LittleEndian ip=(ip Shr 24) | (ip Shr 8 & $ff00) | (ip Shl 8 & $ff0000) | (ip Shl 24) ? Return ip EndFunction Type TNetworkNode Field port:Int Field ip:Int Field enethost:Byte Ptr Field enetpeer:Byte Ptr Method Delete() If enethost enet_host_destroy(enethost) enethost=Null EndIf EndMethod Method Update(callback(client:TClient,id:Int,packet:TPacket)=Null) Local ip:Int,port:Int,client:TClient,ev:ENetEvent=New ENetEvent,id:Byte,packet:TPacket If Not Self.enethost RuntimeError "Can't update a remote server." Repeat If enet_host_service(Self.enethost,ev,0) Select ev.event Case ENET_EVENT_TYPE_CONNECT id=NETWORK_CONNECT Case ENET_EVENT_TYPE_DISCONNECT id=NETWORK_DISCONNECT Case ENET_EVENT_TYPE_RECEIVE Local size:Int=enet_packet_size(ev.packet) Local data:Byte[size] MemCopy(Varptr id,enet_packet_data(ev.packet),1) If size>1 packet=New TPacket packet._bank.resize(size-1) MemCopy(packet._bank.buf(),enet_packet_data(ev.packet)+1,size-1) EndIf Default Continue EndSelect EvaluateEvent(id,packet,ev.peer) Else Exit EndIf Forever EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Abstract EndType Public Const NETWORK_CONNECT:Int=1 Const NETWORK_DISCONNECT:Int=2 Const NETWORK_PINGREQUEST:Int=3 Const NETWORK_PINGRESPONSE:Int=4 Const NETWORK_JOINREQUEST:Int=5 Const NETWORK_JOINRESPONSE:Int=6 Const NETWORK_CHAT:Int=7 Const NETWORK_LEAVEGAME:Int=8 Const NETWORK_CHANGENAMEREQUEST:Int=9 Const NETWORK_CHANGENAMERESPONSE:Int=10 Const NETWORK_PLAYERJOINED:Int=11 Const SEND_RELIABLE:Int=ENET_PACKET_FLAG_RELIABLE Const SEND_UNSEQUENCED:Int=ENET_PACKET_FLAG_UNSEQUENCED Type TServer Extends TNetworkNode Const maxplayers:Int=64 Field clients:TList=New TList Field callback(server:TServer,client:TClient,id:Int,packet:TPacket) Field bannedips:Int[] Field clientmap:TMap=New TMap Method Delete() If enethost enet_host_destroy(enethost) enethost=Null EndIf EndMethod Function Create:TServer(ip:Int=0,port:Int=7777) Local server:TServer=New TServer,addr:Byte Ptr If ip server.ip=ip Else server.ip=ENET_HOST_ANY EndIf server.port=port If server.port<>0 Or server.ip<>ENET_HOST_ANY addr=enet_address_create(server.ip,server.port) EndIf server.enethost=enet_host_create(addr,maxplayers,0,0) If addr enet_address_destroy(addr) EndIf If Not server.enethost Return Null EndIf Return server EndFunction Method FindClientByName:TClient(name:String) Return TClient(clientmap.valueforkey(name)) EndMethod Method FindClientByPeer:TClient(peer:Byte Ptr) Local client:TClient For client=EachIn clients If client.enetpeer=peer Return client Next EndMethod Method FindClient:TClient(ip:Int,port:Int) Local client:TClient For client=EachIn clients If client.ip=ip And client.port=port Return client Next EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Local client:TClient Local issilent:Int=False client=FindClient(enet_peer_ip(enetpeer),enet_peer_port(enetpeer)) Select id Case NETWORK_JOINREQUEST If client Disconnect(client,1) client=TClient.Find(enet_peer_ip(enetpeer),enet_peer_port(enetpeer)) client.enetpeer=enetpeer If IPBanned(client.ip) Disconnect(client,0) issilent=1 Else client.name=packet.ReadLine() If Not FindClientByName(client.name) clientmap.insert(client.name,client) clients.AddLast(client) Local responsepacket:TPacket=New TPacket responsepacket.WriteByte(1) responsepacket.WriteByte(clients.count()) Local peer:TClient For peer=EachIn clients responsepacket.WriteLine(peer.name) Next Send(client,NETWORK_JOINRESPONSE,responsepacket,SEND_RELIABLE) id=NETWORK_PLAYERJOINED Else packet=New TPacket packet.WriteByte(0)'no, you can't joint packet.WriteByte(1)'reason: name already taken Send(client,NETWORK_JOINRESPONSE,packet,SEND_RELIABLE) 'Disconnect(client,0) issilent=1 EndIf EndIf Case NETWORK_CHANGENAMEREQUEST Local name:String=packet.ReadLine() packet=New TPacket If client If FindClientByName(name)<>Null And client.name<>name packet.WriteByte(0) send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE) issilent=True Else packet.WriteByte(1) packet.WriteLine(name) send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE) clientmap.remove(client.name) client.name=name clientmap.insert(name,client) EndIf Else If FindClientByName(name)=Null packet.WriteByte(1) packet.WriteLine(name) send(client,NETWORK_CHANGENAMERESPONSE,packet,SEND_RELIABLE) issilent=True EndIf EndIf Case NETWORK_CONNECT issilent=True Case NETWORK_DISCONNECT client=FindClientByPeer(enetpeer) If client Disconnect(client,0) Else issilent=True EndIf Case NETWORK_PINGREQUEST Send(client,NETWORK_PINGRESPONSE,packet) issilent=True Case NETWORK_CHAT Local relay:TPacket=New TPacket Local count:Int=packet.ReadByte() relay.WriteLine(packet.ReadLine()) If count=0 Broadcast(NETWORK_CHAT,relay) Else For Local n:Int=1 To count client=FindClientByName(packet.ReadLine()) If client Send(client,NETWORK_CHAT,relay) EndIf Next EndIf issilent=True EndSelect If Not issilent If callback If packet packet.seek(0) callback(Self,client,id,packet) EndIf EndIf EndMethod Method Send:Int(client:TClient,id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local enetpacket:Byte Ptr Local result:Int If Not client.enetpeer RuntimeError "Can't send to local client." Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) result=(enet_peer_send(client.enetpeer,channel,enetpacket)=0) Return result EndMethod Method Broadcast:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local result:Int=1 For Local client:TClient=EachIn clients If Not Send(client,id,packet,flags,channel) result=0 Next Return result Rem Local enetpacket:Byte Ptr Local result:Int Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) enet_host_broadcast(Self.enethost,channel,enetpacket) EndRem EndMethod Method Disconnect(client:TClient,force:Int=False) If client.enetpeer If force enet_peer_reset(client.enetpeer) Else enet_peer_disconnect(client.enetpeer) EndIf clients.remove(client) If Not client.enethost client.link.remove() EndIf If clientmap.valueforkey(client.name)=client clientmap.remove(client.name) EndIf EndIf EndMethod Method BanIP(ip:Int) bannedips=bannedips[..bannedips.length+1] bannedips[bannedips.length-1]=ip EndMethod Method IPBanned:Int(ip:Int) For Local n:Int=0 To bannedips.length-1 If ip=bannedips[n] Return True Next Return False EndMethod Method Kick(client:TClient) BanIP(client.ip) Disconnect(client) EndMethod EndType Type TClient Extends TNetworkNode Const maxplayers:Int=64 Global list:TList=New TList Field name:String Field link:TLink Field server:TServer Field connected:Int Field joined:Int=0 Field callback(client:TClient,id:Int,packet:TPacket) Function Find:TClient(ip:Int,port:Int) Local client:TClient For client=EachIn list If client.ip=ip And client.port=port Return client Next client=New TClient client.ip=ip client.port=port client.link=list.addlast(client) Return client EndFunction Function Create:TClient(ip:Int=0,port:Int=7776) Local client:TClient=Find(ip,port) Local addr:Byte Ptr If ip client.ip=ip Else client.ip=ENET_HOST_ANY EndIf client.port=port If client.port<>0 Or client.ip<>ENET_HOST_ANY addr=enet_address_create(client.ip,client.port) client.enethost=enet_host_create(addr,maxplayers,0,0) If addr enet_address_destroy(addr) If Not client.enethost Return Null EndIf Return client EndFunction Method Disconnect(force:Int=False) If server If force enet_peer_reset(server.enetpeer) Else enet_peer_disconnect(server.enetpeer) EndIf server=Null EndIf EndMethod Method SetName(name:String) If name.length>16 name=name[..16] If server Local packet:TPacket=New TPacket packet.WriteLine(name) Send(NETWORK_CHANGENAMEREQUEST,packet,SEND_RELIABLE) Else Self.name=name EndIf EndMethod Method Connect:Int(ip:Int,port:Int) Local addr:Byte Ptr If Not Self.enethost RuntimeError "Remote client cannot connect to server." If server Disconnect() server=New TServer server.ip=ip server.port=port addr=enet_address_create(server.ip,server.port) server.enetpeer=enet_host_connect(Self.enethost,addr,1) enet_address_destroy(addr) If server.enetpeer=Null server=Null Return 0 EndIf Return 1 EndMethod Method Send:Int(id:Int,packet:TPacket=Null,flags:Int=0,channel:Int=0) Local enetpacket:Byte Ptr Local result:Int If Not connected Return 0 If Not server RuntimeError "Client is not connected." Local data:Byte[] If packet If packet._bank.size()=0 packet=Null EndIf If packet data=New Byte[packet._bank.size()+1] MemCopy(Varptr data[1],packet._bank.buf(),packet._bank.size()) Else data=New Byte[1] EndIf data[0]=id enetpacket=enet_packet_create(data,data.length,flags) result=(enet_peer_send(server.enetpeer,channel,enetpacket)=0) Return result EndMethod Method EvaluateEvent(id:Int,packet:TPacket,enetpeer:Byte Ptr) Local issilent:Int=False Select id Case NETWORK_CONNECT Self.connected=True packet=New TPacket packet.WriteLine(name) Send(NETWORK_JOINREQUEST,packet) Case NETWORK_JOINRESPONSE joined=packet.ReadByte() Case NETWORK_CHANGENAMERESPONSE If packet.ReadByte()=1 name=packet.ReadLine() EndIf Case NETWORK_PINGRESPONSE Case NETWORK_CHAT EndSelect If Not issilent If callback If packet packet.seek(0) callback(Self,id,packet) EndIf EndIf EndMethod Method Join() If Not joined Local packet:TPacket=New TPacket packet.WriteLine(name) Send(NETWORK_JOINREQUEST,packet) EndIf EndMethod Method Ping:Int() Local packet:TPacket=New TPacket packet.WriteInt(MilliSecs()) Return Send(NETWORK_PINGREQUEST,packet) EndMethod Method Say:Int(text:String,recipients:String[]=Null) Local packet:TPacket=New TPacket If recipients packet.WriteByte(recipients.length) packet.WriteLine(text) For Local n:Int=0 To recipients.length-1 packet.WriteLine(recipients[n]) Next Else packet.WriteByte(0) packet.WriteLine(text) EndIf Return Send(NETWORK_CHAT,packet) EndMethod EndType Type TPacket Extends TBankStream Method New() _bank=New TBank EndMethod EndType Function CreatePacket:TPacket() Local packet:TPacket=New TPacket Return packet EndFunction '---------------------------------------------------------------------------------------------------- Local server:TServer Local client1:TClient Local client2:TClient Local ip:Int=HostIp("127.0.0.1") server=TServer.Create(ip,7777) client1=TClient.Create(ip,7776) client2=TClient.Create(ip,7779) client1.name="Steve" client2.name="Bob" client1.connect(server.ip,server.port) client2.connect(server.ip,server.port) client1.callback=ClientCallBack client2.callback=ClientCallBack server.callback=ServerCallBack Repeat server.Update() client1.Update() client2.Update() Delay 1 If Rand(100)=1 client1.SetName("Barney") client1.Disconnect() 'client2.Ping() client2.Say("Hello!") EndIf Forever Function ServerCallBack(server:TServer,client:TClient,id:Int,packet:TPacket) Select id Case NETWORK_PLAYERJOINED Print "New player "+packet.ReadLine()+" joined." Case NETWORK_DISCONNECT If client Print client.name+" disconnected." Else Print "Unknown disconnection" Print server.clients.count()+" players" EndIf Case NETWORK_CONNECT Print "New client connected." EndSelect EndFunction Function ClientCallBack(client:TClient,id:Int,packet:TPacket) Select id Case NETWORK_CHANGENAMERESPONSE If packet.ReadByte()=1 Print "Changed name to "+packet.ReadLine() Else Print "Name change rejected." EndIf Case NETWORK_JOINRESPONSE If packet.ReadByte()=1 Print "Joined game" Local count:Int=packet.ReadByte() Print count+" players" For Local n:Int=1 To count Print n+". "+packet.ReadLine() Next Else Print "Rejected from game" Select packet.ReadByte() Case 1 Print "Name already in use" Default Print "Unknown reason" EndSelect EndIf Case NETWORK_DISCONNECT Print "Disconnected from server." Case NETWORK_CONNECT Print "Connected to server." Case NETWORK_CHAT Print "Says: "+packet.ReadLine() Case NETWORK_PINGRESPONSE Print "Ping = "+(MilliSecs()-packet.ReadInt()) EndSelect EndFunction 1 Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Rekindled Phoenix Posted January 10, 2010 Share Posted January 10, 2010 Wow this is great! Thanks for providing this template Josh, this is really helpful when you are creating multi-player games. Quote Link to comment Share on other sites More sharing options...
Rick Posted January 11, 2010 Share Posted January 11, 2010 This is probably just preference, but I also liked mapping messages to functions instead of having 1 huge callback function for all the events with 1 huge case statement. I just felt it makes the code cleaner. That's starting to look like Win32 API programming with the case statement. Quote Link to comment Share on other sites More sharing options...
Josh Posted January 11, 2010 Author Share Posted January 11, 2010 What do you mean by "mapping messages to functions"? Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Rick Posted January 11, 2010 Share Posted January 11, 2010 In lua terms it would be a table of functions where the key is the message id. I guess it's nothing we couldn't do ourselves, but would be cool to have it part of the code. So instead of: Function ClientCallBack(client:TClient,id:Int,packet:TPacket) Select id Case NETWORK_CHANGENAMERESPONSE If packet.ReadByte()=1 Print "Changed name to "+packet.ReadLine() Else Print "Name change rejected." EndIf Case NETWORK_JOINRESPONSE If packet.ReadByte()=1 Print "Joined game" Local count:Int=packet.ReadByte() Print count+" players" For Local n:Int=1 To count Print n+". "+packet.ReadLine() Next Else Print "Rejected from game" Select packet.ReadByte() Case 1 Print "Name already in use" Default Print "Unknown reason" EndSelect EndIf Case NETWORK_DISCONNECT Print "Disconnected from server." Case NETWORK_CONNECT Print "Connected to server." Case NETWORK_CHAT Print "Says: "+packet.ReadLine() Case NETWORK_PINGRESPONSE Print "Ping = "+(MilliSecs()-packet.ReadInt()) EndSelect EndFunction So the users would define a function and assign it to the message function NameChange(client, packet) end networkMessage[NETWORK_CHANGENAMERESPONSE] = NameChange Then the library message pump would check the message id to see if it exists in the table and if so call the function the user defined for it. networkMessage[id](client, packet) The users could still do this even if you have 1 callback function anyway I guess. This just seems more "event" driven. Quote Link to comment Share on other sites More sharing options...
BLaBZ Posted September 14, 2010 Share Posted September 14, 2010 Is there any limit to the number of lines/ints/variables you can write to a single packet? Quote = OpenGL Link to comment Share on other sites More sharing options...
AggrorJorn Posted September 18, 2010 Share Posted September 18, 2010 Never noticed this topic before but is looks awesome. Question: Is this working LE code for 2.4? or is it a preferable way how networking should be designed or is going to be designed for LE 3.0? If this is the officaly supported package I deffinetly want to try it out. Quote Link to comment Share on other sites More sharing options...
BLaBZ Posted September 30, 2010 Share Posted September 30, 2010 Should this work on linux and macs? Quote = OpenGL Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.