Entering ingame

This commit is contained in:
Amazed 2019-02-15 01:49:34 +01:00
parent 92ebbdf64c
commit 556c9dcc69
5 changed files with 215 additions and 17 deletions

View File

@ -66,7 +66,6 @@ class Netserver(asyncio.DatagramProtocol):
pass
def datagram_received(self, data, addr):
logger.debug("%s:%s > %s", addr[0], addr[1], data.hex())
loop = asyncio.get_event_loop()
loop.create_task(self.handle_new_packet(addr, data))
@ -78,6 +77,7 @@ class Netserver(asyncio.DatagramProtocol):
elif len(bData) >= 12 and first & CFrame.PACKET_COMMAND_FRAME:
await self.handle_new_cframe(addr, pPacket)
elif first == EnumQuery.LEAD or first == EnumResponse.LEAD:
logger.debug("%s:%s > %s (ENUM)", addr[0], addr[1], bData.hex())
await self.handle_new_enum(addr, pPacket)
else:
logger.error("Unknown frame received")
@ -90,7 +90,7 @@ class Netserver(asyncio.DatagramProtocol):
# EnumQuery
try:
eq = EnumQuery(data)
logger.debug("Got EnumQuery, sending EnumResponse")
#logger.debug("Got EnumQuery, sending EnumResponse")
er = EnumResponse()
er.Payload = eq.Payload
er.ApplicationDescSize = 0x50
@ -109,6 +109,7 @@ class Netserver(asyncio.DatagramProtocol):
# \x00\x00\x00\x01: <custom map>
# \x9c\x53\xf4\xdd: Three Way Island - Canyons
# \x1e\xe9\x39\xe1: Still Winter
# \x9f\xb2\x42\xec: test
er.ApplicationReservedData += self.server.currentmap.mapname.encode("ascii")'''
appdata = Packet()
@ -123,7 +124,7 @@ class Netserver(asyncio.DatagramProtocol):
appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client
appdata.putShort(self.server.detente_time)
appdata.write(
b'\x9c\x53\xf4\xdd') # Seems to be a checksum of current map OR linked to the number of chars in the map name
b'\x9f\xb2\x42\xec') # Seems to be a checksum of current map OR linked to the number of chars in the map name
appdata.write(self.server.currentmap.mapname.encode("ascii"))
appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
er.ApplicationReservedData = appdata.getvalue()
@ -147,6 +148,7 @@ class Netserver(asyncio.DatagramProtocol):
session = self.get_session(addr)
player = self.get_player(session) if session else None
if cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECT:
logger.debug("%s:%s > %s (CFRAME CONNECT)", addr[0], addr[1], data.getvalue().hex())
# CONNECT CFRAME
if session and session.Full:
logger.error("Session %s:%s was already fully connected. Ignoring.", session.ip, session.port)
@ -182,6 +184,7 @@ class Netserver(asyncio.DatagramProtocol):
self.server.create_temp_player(player)
#session.setup_Connect_Retry_Timer()
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_CONNECTED:
logger.debug("%s:%s > %s (CFRAME CONNECTED)", addr[0], addr[1], data.getvalue().hex())
# CONNECTED CFRAME
# check if already sent a CONNECT packet
logger.debug("%s:%s sent back a CONNECTED CFrame.", session.ip, session.port)
@ -198,6 +201,7 @@ class Netserver(asyncio.DatagramProtocol):
session.send_dframe_keepalive()
elif cframe.ExtOpCode == CFrame.FRAME_EXOPCODE_SACK:
logger.debug("%s:%s > %s (CFRAME SACK)", addr[0], addr[1], data.getvalue().hex())
if not session or not session.Full:
logger.error("Received a SACK packet for a non fully connected session")
return
@ -227,6 +231,7 @@ class Netserver(asyncio.DatagramProtocol):
return
#logger.debug("Got a CFrame POLL. Replying with a SACK.")
# must send back a ACK
session.send_cframe_sack()
except Exception:
logger.error("Should have been a CFRAME but could not parse it")
@ -236,7 +241,7 @@ class Netserver(asyncio.DatagramProtocol):
async def handle_new_dframe(self, addr, data):
try:
dframe = DFrame(data)
logger.debug("Received DFRAME")
#logger.debug("Received DFRAME")
session = next((x for x in self.addrs if x.ip == addr[0] and x.port == addr[1]), None)
if not session:
logger.debug("%s sent a DFRAME without having sent a CONNECT before. GTFO.", addr)
@ -267,6 +272,11 @@ class Netserver(asyncio.DatagramProtocol):
pass
#session.send_dframe_keepalive()
if dframe.Command & DFrame.PACKET_COMMAND_POLL:
#logger.debug("Sending SACK")
#session.send_cframe_sack()
pass
session.next_expected_seq += 1
if dframe.Payload:
await self.handle_game_packet(session, dframe.Payload)
@ -279,6 +289,9 @@ class Netserver(asyncio.DatagramProtocol):
async def handle_game_packet(self, session, payload):
player = self.get_temp_player(session)
logger.debug("%s:%s > %s (GAMEDATA)", session.ip, session.port, payload.hex())
#logger.debug("OPCODE: %s VALUES: %s", struct.pack("<B", payload[0]).hex(), payload[1:].hex())
#await self.server.broadcast_message("SENT OPCODE: %s VALUES: %s" % (struct.pack("<B", payload[0]).hex(), payload[1:].hex()))
if payload[0] == 0xc1:
if not player or not player.phase == PlayerPhases.CFRAME_CONNECTED:
@ -301,7 +314,7 @@ class Netserver(asyncio.DatagramProtocol):
dwURLSize = ppayload.getULong()
guidInstance = payload[52:68]
guidApplication = payload[68:84]
name = payload[dwNameOffset+4:dwNameOffset+4+dwNameSize]
name = payload[dwNameOffset+4:dwNameOffset+2+dwNameSize]
name = name.decode("utf-16-le")
logger.debug("%s connected!", name)
player.name = name
@ -323,7 +336,7 @@ class Netserver(asyncio.DatagramProtocol):
appdata.putShort(self.server.points_per_kill)
appdata.write(b'\x00\x00') # original: 0000 - does not seem to affect client
appdata.putShort(self.server.detente_time)
appdata.write(b'\x9c\x53\xf4\xdd') # Seems to be a checksum of current map OR linked to the number of chars in the map name
appdata.write(b'\x9f\xb2\x42\xec') # Seems to be a checksum of current map OR linked to the number of chars in the map name
appdata.write(self.server.currentmap.mapname.encode("ascii"))
appdata.write(b'\x00' * (32 - len(self.server.currentmap.mapname)))
d.ApplicationReservedData = appdata.getvalue()
@ -339,7 +352,7 @@ class Netserver(asyncio.DatagramProtocol):
await self.server.add_player(player)
#player.session.send_cframe_sack()
await player.session.send_gamedata(b'\x3c'+struct.pack("<L", player.id)+b"\x00")
await player.session.send_gamedata(b'\x3c'+struct.pack("<L", player.id)+b"\x00", acknow=True)
p1 = b'\x01' + struct.pack("<B", len(self.server.players))
for pplayer in self.server.players:
@ -356,8 +369,8 @@ class Netserver(asyncio.DatagramProtocol):
p1 += b'\x00\x00'
await player.session.send_gamedata(p1, acknow=False)
await player.session.send_gamedata(b'\x3d\x00\x5b\x53\x65\x72\x76\x65\x72\x5d'+b"\x00"*25) # [SERVER]
await player.session.send_gamedata(b'\x3d\x01'+player.name.encode("ascii")+b"\x00"*(33-len(player.name.encode("ascii")))) # playername
await player.session.send_gamedata(b'\x3d\x00\x5b\x53\x65\x72\x76\x65\x72\x5d'+b"\x00"*25, acknow=True) # [SERVER]
await player.session.send_gamedata(b'\x3d\x01'+player.name.encode("ascii")+b"\x00"*(33-len(player.name.encode("ascii"))), acknow=True) # playername
"""
player.session.send_gamedata(b'\x0f\x56\xab\x31\x96\x06\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
player.session.send_gamedata(b'\x10\x02\x56\xab\x31\x96\x00\x00\x00\x00\x00\x00\x00\x00') # unknown
@ -379,14 +392,183 @@ class Netserver(asyncio.DatagramProtocol):
player.session.send_gamedata(b'\x0a\x12\x00\x00\x00\x00\x2e\x10\x03\x10\x12\x00\x00\x00\xa4\xfd\x2c\x03\x00',acknow=True) # unknown
player.session.send_gamedata(b'\x2f\x02\x00\x00\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00', acknow=True) # unknown
"""
else:
logger.debug("OPCODE: %s VALUES: %s", struct.pack("<B", payload[0]).hex(), payload[1:].hex())
await self.server.broadcast_message("SENT OPCODE: %s VALUES: %s" % (struct.pack("<B", payload[0]).hex(), payload[1:].hex()))
elif payload[0] == 0x0f:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
inc = Packet(payload)
opcode = inc.getByte()
playerid = inc.getULong()
status = inc.getByte()
if status == 7:
status = 8
out = Packet()
out.putByte(opcode)
out.putULong(playerid)
out.putByte(status)
await player.session.send_gamedata(out.getvalue(), acknow=False)
elif payload[0] == 0x2c:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
# assign team?
await player.session.send_gamedata(b"\x10"+struct.pack("<B", player.team)+struct.pack("<L", player.id)+b"\x00\x00", acknow=False)
elif payload[0] == 0x2a:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
# winning condition time limit
time_limit_minutes = 60 # 1 hour
await player.session.send_gamedata(b"\x29"+struct.pack("<H", time_limit_minutes)+b"\x00\x80"+(b"\x00"*17), acknow=True)
await player.session.send_gamedata(b"\x39"+(b"\x00"*21), acknow=False) # acknow=True
await player.session.send_gamedata(b"\x39\x01\x01\x01\x01"+(b"\x00"*17), acknow=True)
elif payload[0] == 0x0c:
player = self.get_player(session)
if not player:
logger.debug("Fuck that no player wtf man")
return
playerping = 1
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", playerping) + b"\x00",acknow=False) # acknow=True
for i in range(0x06):
ack = (i % 2 == 0)
await player.session.send_gamedata(b"\x04" + struct.pack("<B", i) + b"\x00\x00\x00\x00" + struct.pack("<B", i+0x07) + b"\x00\x00\x00",acknow=ack)
await player.session.send_gamedata(b"\x1a\xee\x02\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x83\x5f\xa4\x44\x00\xf9\xa3\xc2\x00\x00\x00\x00\xff\xff\x33\x43\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x9e\xdc\x07\x00", acknow=True)
await player.session.send_gamedata(b"\x19\x96\x03\x00\x10\x00\x00\x04\x00\x0d\x00\x00\x83\x5f\xa4\x44\x00\xf9\xa3\xc2\x00\x00\x00\x00\xff\xff\x33\x43\x00\x00\x7a\x44\x00\x00\x00\x00\x00\x00\x00\x00\x00", acknow=False)
await player.session.send_gamedata(b"\x39"+b"\x00"*21, acknow=True)
await player.session.send_gamedata(b"\x39"+b"\x01"*4+b"\x00"*17, acknow=False)
await player.session.send_gamedata(b"\x2e"+b"\x00"*9, acknow=True)
await player.session.send_gamedata(b"\x3b\x00\x04\x00\x00\x00\x04\x00\x00\x00", acknow=False)
await player.session.send_gamedata(b"\x3e\x00\x00\x00\x00\x00", acknow=True)
await player.session.send_gamedata(b"\x3f\x00\x00", acknow=False)
await player.session.send_gamedata(b"\x0e\x00", acknow=True)
await player.session.send_gamedata(b"\x10" + struct.pack("<B", player.team) + struct.pack("<L", player.id) + b"\x00\x00", acknow=False)
return
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\x71\xa6\x08\x10\x12\x00\x00\x00\xde\xfe\x83\xfd\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\x68\xa6\x08\x10\x12\x00\x00\x00\x50\xff\x5a\xfd\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(b"\x4b\xe0\xe8\x0c\x00\x00\x8a\x14\x00\x00", acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\xa9\xa5\x08\x01\x2d\x00\x00\x00\x25\x54\x05\x46\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x16\x00\x00\x00\x00\x53\xa6\x08\x04\x15\x00\x00\x00\xc2\xfd\x6d\x01\x40\x00\xf4\xff\x0a\x14\x00\x00\x00\x00\x71\xa6\x08\x01\x12\x00\x00\x00\xde\xfe\x81\xfd\x00\x00\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x14\x00\x00\x00\x00\x68\xa6\x08\x01\x12\x00\x00\x00\x51\xff\x5c\xfd\x00\x00\x00",
acknow=False) # acknow=True
playerping = 1
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", playerping) + b"\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\x57\xa6\x08\x10\x12\x00\x00\x00\x2e\xff\xa7\xfd\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x14\x00\x00\x00\x00\x57\xa6\x08\x01\x12\x00\x00\x00\x5b\xff\x75\xfd\x00\x00\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x16\x00\x00\x00\x00\x50\xa6\x08\x04\x15\x00\x00\x00\xe2\xfd\x51\x01\x3d\x00\xed\xff\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x16\x00\x00\x00\x00\x55\xa6\x08\x04\x15\x00\x00\x00\xe5\xfd\x40\x01\xe5\xff\xf8\xff\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\x72\xa6\x08\x10\x12\x00\x00\x00\xf2\xfe\x79\xfd\x0a\x14\x00\x00\x00\x00\x72\xa6\x08\x01\x12\x00\x00\x00\x28\xff\x48\xfd\x00\x00\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x16\x00\x00\x00\x00\x4e\xa6\x08\x04\x15\x00\x00\x00\xc8\xfd\x2f\x01\xa1\xff\x06\x00\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(
b"\x0a\x12\x00\x00\x00\x00\xa9\xa5\x08\x01\x2d\x00\x00\x00\x19\x3d\x09\x46\x00",
acknow=False) # acknow=True
await player.session.send_gamedata(bytes.fromhex("0a120000000057a608101200000031ffa4fd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a120000000057a608101200000031ffa4fd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000056a6080415000000b1fd4001c8ff120000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000056a6080415000000b1fd4001c8ff120000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a12000000005ca608101200000077ff03fe00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a14000000005ca608011200000050ffd6fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000047a6080415000000bafd1001d0ff0a0000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a120000000072a6081012000000f5fe75fd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000052a6080415000000c9fd45011800080000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a16000000004ca6080415000000c1fdab01e7ff0d0000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a140000000057a608011200000030ffa6fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("2b00000000fa5e5b4500"), acknow=False)
await player.session.send_gamedata(bytes.fromhex(
"0a12000000005ca608101200000074fffffd0a12000000005da608101200000092ff6afd0a140000000072a6080112000000f4fe77fd00000a14000000005da6080112000000a7ff80fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a1200000000a9a508012d000000fe280d4600"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("091200000000c9a50802260000000073a60800"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a140000000073a608011200000009ff97fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("2e070700000000000000"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000054a6080415000000befd88010d00e4ff00"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a16000000004aa6080415000000b0fd5301b4ffb4ff00"),
acknow=False)
await player.session.send_gamedata(
bytes.fromhex("0a120000000068a608101200000075ff7dfd0a140000000068a608011200000051ff5cfd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a12000000005da608101200000095ff6dfd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex(
"0a16000000004ba6080415000000e7fd3f01e0ffa6ff0a160000000049a6080415000000b5fd6101c0ffe5ff00"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a14000000005ca608011200000076ff00fe000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a120000000068a608101200000071ff79fd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("090e00000000c9a508012600000000"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a1200000000a9a508012d000000c016114600"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a16000000004fa6080415000000b2fd8801c9ffceff00"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a14000000005da608011200000094ff6cfd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a160000000048a608041500000011fe5201e2ff2c0000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a120000000073a6081012000000c0fe5efd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a140000000073a608011200000009ff83fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a120000000071a6081012000000fbfec2fd00"), acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a140000000071a6080112000000defe81fd000000"),
acknow=False)
await player.session.send_gamedata(bytes.fromhex("0a140000000068a608011200000072ff7afd000000"),
acknow=False)
for i in range(0x7f):
if i >= 0x5c:
await player.session.send_gamedata(bytes.fromhex(
"04" + struct.pack("<B", i).hex() + "00000000" + struct.pack("<B", (
0xa4 + i) % 0xff).hex() + "a60800"), acknow=False)
else:
await player.session.send_gamedata(bytes.fromhex(
"04" + struct.pack("<B", i).hex() + "00000000" + struct.pack("<B", 0xa4 + i).hex() + "a50800"),
acknow=False)
await player.session.send_gamedata(b"\x05\x01\x7b\x02\x00\x03\x00\x00\x00\x00", acknow=False) # acknow=True
# 0f packet
# final packet
await player.session.send_gamedata(b"\x2b\x00\x00\x00\x00\xa0\xe8\x5a\x45\x00", acknow=False) # acknow=True
def send_packet(self, addr, packet):
self.remotesocket.sendto(packet.getvalue(), addr)
logger.debug("%s:%s < %s", addr[0], addr[1], packet.getvalue().hex())
#logger.debug("%s:%s < %s", addr[0], addr[1], packet.getvalue().hex())
def get_session(self, iporaddr, port=None):
if not port:

View File

@ -4,7 +4,7 @@ import struct
class Packet(io.BytesIO):
def putByte(self, val):
self.write(struct.pack('<B', val))
self.write(struct.pack('<B', val % 256))
def getByte(self):
return struct.unpack('<B', self.read(1))[0]

View File

@ -25,7 +25,6 @@ class Session:
rawpacket = packet.to_packet()
rawbytes = rawpacket.getvalue()
self.serversock.sendto(rawbytes, (self.ip, self.port))
logger.debug("%s:%s < %s", self.ip, self.port, rawbytes.hex())
if len(rawbytes) >= 4 and rawbytes[0] & DFrame.PACKET_COMMAND_DATA:
self.next_send += 1
@ -61,6 +60,7 @@ class Session:
dframe.Seq = self.next_send
dframe.NRcv = self.next_expected_seq
dframe.Payload = bPayload
logger.debug(" %s:%s < %s (GAMEDATA)", self.ip, self.port, bPayload.hex())
self.send(dframe)
if acknow:
await self.lock.acquire()
@ -71,6 +71,7 @@ class Session:
response.ExtOpCode = CFrame.FRAME_EXOPCODE_CONNECTED
response.RspId = connect.MsgID
response.SessID = connect.SessID
logger.debug(" %s:%s < %s (CFRAME CONNECTED)", self.ip, self.port, response.to_packet().getvalue().hex())
self.send(response)
def send_cframe_sack(self):
@ -80,6 +81,7 @@ class Session:
r.Flags = CFrame.SACK_FLAGS_RESPONSE
r.NRecv = self.next_expected_seq
r.NSeq = self.next_send
logger.debug(" %s:%s < %s (CFRAME SACK)", self.ip, self.port, r.to_packet().getvalue().hex())
self.send(r)
def send_dframe_keepalive(self):

View File

@ -1,9 +1,23 @@
import random
import asyncio
import struct
class Greetings:
def __init__(self, server):
self.server = server
self.messages = ["Welcome %s!", "Beware! %s has arrived!", "A wild %s appears", "%s has come and is ready to pwn."]
async def on_player_join(self, player):
await self.server.broadcast_message("Welcome "+player.name+"!")
await self.server.broadcast_message(random.choice(self.messages) % player.name)
#await Greetings.send_ping(player)
@staticmethod
async def send_ping(player):
while 1:
for ping in range(0, 999):
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", ping) + b"\x00",acknow=True) # acknow=True
for ping in range(999, 0, -1):
await player.session.send_gamedata(b"\x2f\x02\x00\x00" + struct.pack("<H", ping) + b"\x00",acknow=True) # acknow=True
async def on_map_change(self, newmap):
await self.server.broadcast_message("You are now playing on "+newmap.mapname)

View File

@ -101,7 +101,7 @@ class Server:
for player in self.players:
if player.name == "[Server]":
continue
await player.session.send_gamedata(b'\x35\x80\x81'+text.encode("ascii")+b'\x00\x00')
await player.session.send_gamedata(b'\x35\x80\x81'+text.encode("ascii")+b'\x00\x00', acknow=False)
if __name__ == '__main__':
server = Server(name="giantsd", maxplayers=20, register=False)