├── README.md ├── datastream ├── bitstream.py └── bytestream.py ├── main.py ├── network ├── connection.py ├── messaging.py └── tcpserver.py ├── protocol ├── messageFactory.py ├── messages │ ├── clientHello.py │ └── serverHello.py └── piranhamessage.py └── screens └── console.png /README.md: -------------------------------------------------------------------------------- 1 | # brawlserver-core 2 | An open source brawl server core! 3 | 4 | ## Requirements 5 | - a brain 6 | - Python 3 7 | 8 | ## How to start 9 | - Just run main.py 10 | 11 | ## How to play 12 | - This is a core of server. You need to implement all messages and logic by yourself 🙃 13 | 14 | ![screenshot](/screens/console.png?raw=true) 15 | -------------------------------------------------------------------------------- /datastream/bitstream.py: -------------------------------------------------------------------------------- 1 | # this bitstream by xeondev1337 2 | 3 | class BitStream: 4 | def __init__(self, buff=b''): 5 | self.buffer = buff 6 | self.bitIndex = 0 7 | self.offset = 0 8 | 9 | def readBit(self): 10 | if self.offset > len(self.buffer): 11 | print("Out of range!") 12 | return 0 13 | value = (self.buffer[self.offset] >> self.bitIndex) & 1 14 | self.bitIndex += 1 15 | if (self.bitIndex == 8): 16 | self.bitIndex = 0 17 | self.offset += 1 18 | return value 19 | 20 | def readBytes(self, length): 21 | data = [] 22 | i = 0 23 | while i < length: 24 | value = 0 25 | p = 0 26 | while p < 8 and i < length: 27 | value |= self.readBit() << p 28 | i += 1 29 | p += 1 30 | data.append(value) 31 | return bytes(data) 32 | 33 | def readPositiveInt(self, bitsCount): 34 | data = self.readBytes(bitsCount) 35 | return int.from_bytes(data, "little") 36 | 37 | def readInt(self, bitsCount): 38 | v2 = 2 * self.readPositiveInt(1) - 1 39 | return v2 * self.readPositiveInt(bitsCount) 40 | 41 | def readPositiveVIntMax255(self): 42 | v2 = self.readPositiveInt(3) 43 | return self.readPositiveInt(v2) 44 | 45 | def readPositiveVIntMax65535(self): 46 | v2 = self.readPositiveInt(4) 47 | return self.readPositiveInt(v2) 48 | 49 | def readPositiveVIntMax255OftenZero(self): 50 | if self.readBoolean(): return 0 51 | return self.readPositiveVIntMax255() 52 | 53 | def readPositiveVIntMax65535OftenZero(self): 54 | if self.readBoolean(): return 0 55 | return self.readPositiveVIntMax65535() 56 | 57 | def readBoolean(self): 58 | return self.readPositiveInt(1) == 1 59 | 60 | def writeBit(self, data): 61 | if (self.bitIndex == 0): 62 | self.offset += 1 63 | self.buffer += b'\xff' 64 | 65 | value = self.buffer[self.offset - 1] 66 | value &= ~(1 << self.bitIndex) 67 | value |= (data << self.bitIndex) 68 | self.buffer[self.offset - 1] = value 69 | 70 | self.bitIndex = (self.bitIndex + 1) % 8 71 | 72 | def writeBits(self, bits, count): 73 | i = 0 74 | position = 0 75 | while i < count: 76 | value = 0 77 | 78 | p = 0 79 | while p < 8 and i < count: 80 | value = (bits[position] >> p) & 1 81 | self.writeBit(value) 82 | p += 1 83 | i += 1 84 | position += 1 85 | 86 | def writePositiveInt(self, value, bitsCount): 87 | self.writeBits(value.to_bytes(4, byteorder='little'), bitsCount) 88 | 89 | def writeInt(self, value, bitsCount): 90 | val = value 91 | if val <= -1: 92 | self.writePositiveInt(0, 1) 93 | val = -value 94 | elif val >= 0: 95 | self.writePositiveInt(1, 1) 96 | val = value 97 | self.writePositiveInt(val, bitsCount) 98 | 99 | def writePositiveVInt(self, value, count): 100 | v3 = 1 101 | v7 = value 102 | 103 | if v7 != 0: 104 | if (v7 < 1): 105 | v3 = 0 106 | else: 107 | v8 = v7 108 | v3 = 0 109 | 110 | v3 += 1 111 | v8 >>= 1 112 | 113 | while (v8 != 0): 114 | v3 += 1 115 | v8 >>= 1 116 | self.writePositiveInt(v3 - 1, count) 117 | self.writePositiveInt(v7, v3) 118 | 119 | def writePositiveVIntMax255(self, value:int): 120 | self.writePositiveVInt(value, 3) 121 | 122 | def writePositiveVIntMax65535(self, value:int): 123 | self.writePositiveVInt(value, 4) 124 | 125 | def writePositiveVIntMax255OftenZero(self, value:int): 126 | if value == 0: 127 | self.writePositiveInt(1, 1) 128 | return 129 | self.writePositiveInt(0, 1) 130 | self.writePositiveVInt(value, 3) 131 | 132 | def writePositiveVIntMax65535OftenZero(self, value:int): 133 | if value == 0: 134 | self.writePositiveInt(1, 1) 135 | return 136 | self.writePositiveInt(0, 1) 137 | self.writePositiveVInt(value, 4) 138 | 139 | def writeBoolean(self, value:bool): 140 | if value: self.writePositiveInt(1, 1) 141 | else: self.writePositiveInt(0, 1) 142 | 143 | -------------------------------------------------------------------------------- /datastream/bytestream.py: -------------------------------------------------------------------------------- 1 | 2 | class ByteStream: 3 | def __init__(self, buffer=b''): 4 | self.buffer = bytearray(buffer) 5 | self.offset = 0 6 | self.bitIdx = 0 7 | self.currentByte = 0 8 | 9 | def readBytes(self, length): 10 | array = self.buffer[self.offset:self.offset+length] 11 | self.offset += length 12 | return array 13 | 14 | def readByte(self): 15 | self.bitIdx = 0 16 | return int.from_bytes(self.readBytes(1), "big", signed=True) 17 | 18 | def readInt(self): 19 | self.bitIdx = 0 20 | return int.from_bytes(self.readBytes(4), "big") 21 | 22 | def readBoolean(self): 23 | if self.bitIdx == 0: 24 | self.currentByte = int.from_bytes(self.readBytes(1), "big", signed=True) 25 | 26 | result = ((1 << self.bitIdx) & self.currentByte) != 0 27 | self.bitIdx = (self.bitIdx + 1) & 7 28 | return result 29 | 30 | def readString(self, max=900000): 31 | length = self.readInt() 32 | if length < 0 and length > max: 33 | return None 34 | return self.readBytes(length).decode("utf-8") 35 | 36 | def writeBytes(self, array): 37 | self.bitIdx = 0 38 | self.buffer += array 39 | self.offset += len(array) 40 | 41 | def writeInt(self, value:int): 42 | self.writeBytes(int.to_bytes(value, 4, "big")) 43 | 44 | def writeByte(self, value:int): 45 | self.writeBytes(int.to_bytes(value, 1, "big")) 46 | 47 | def writeString(self, value:str): 48 | array = value.encode("utf-8") 49 | self.writeInt(len(array)) 50 | self.writeBytes(array) 51 | 52 | def writeBoolean(self, value:bool): 53 | if self.bitIdx == 0: 54 | self.buffer += bytearray(1) # ensure 55 | 56 | if value: 57 | self.buffer[self.offset] |= (1 << self.bitIdx) 58 | 59 | self.bitIdx = (self.bitIdx + 1) & 7 60 | 61 | def convert_unsigned_to_signed(unsigned:int): # python is dudnik lang :upside_down: 62 | return int.from_bytes(int.to_bytes(unsigned, 4, "big"), "big", signed=True) 63 | 64 | def readVInt(self): 65 | byte = self.readByte() 66 | 67 | if (byte & 0x40) != 0: 68 | result = byte & 0x3f 69 | if (byte & 0x80) != 0: 70 | byte = self.readByte() 71 | result |= (byte & 0x7f) << 6 72 | if (byte & 0x80) != 0: 73 | byte = self.readByte() 74 | result |= (byte & 0x7f) << 13 75 | if (byte & 0x80) != 0: 76 | byte = self.readByte() 77 | result |= (byte & 0x7f) << 20 78 | if (byte & 0x80) != 0: 79 | byte = self.readByte() 80 | result |= (byte & 0x7f) << 27 81 | return ByteStream.convert_unsigned_to_signed(result | 0x80000000) 82 | return ByteStream.convert_unsigned_to_signed(result | 0xF8000000) 83 | return ByteStream.convert_unsigned_to_signed(result | 0xFFF00000) 84 | return ByteStream.convert_unsigned_to_signed(result | 0xFFFFE000) 85 | return ByteStream.convert_unsigned_to_signed(result | 0xFFFFFFC0) 86 | else: 87 | result = byte & 0x3f 88 | 89 | if (byte & 0x80) != 0: 90 | byte = self.readByte() 91 | result |= (byte & 0x7f) << 6 92 | if (byte & 0x80) != 0: 93 | byte = self.readByte() 94 | result |= (byte & 0x7f) << 13 95 | if (byte & 0x80) != 0: 96 | byte = self.readByte() 97 | result |= (byte & 0x7f) << 20 98 | if (byte & 0x80) != 0: 99 | byte = self.readByte() 100 | result |= (byte & 0x7f) << 27 101 | return result 102 | 103 | def writeVInt(self, value:int): 104 | tmp = (value >> 25) & 0x40 105 | flipped = value ^ (value >> 31) 106 | 107 | tmp |= value & 0x3F 108 | value >>= 6 109 | flipped >>= 6 110 | 111 | if flipped == 0: 112 | self.writeByte(tmp) 113 | return 114 | 115 | self.writeByte(tmp | 0x80) 116 | flipped >>= 7 117 | r = 0 118 | if flipped != 0: 119 | r = 0x80 120 | 121 | self.writeByte((value & 0x7F) | r) 122 | value >>= 7 123 | 124 | while flipped != 0: 125 | flipped >>= 7 126 | r = 0 127 | if flipped != 0: 128 | r = 0x80 129 | self.writeByte((value & 0x7F) | r) 130 | value >>= 7 131 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from network.tcpserver import TcpServer 2 | 3 | TcpServer(("0.0.0.0", 9339)).start_accept() 4 | -------------------------------------------------------------------------------- /network/connection.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | from network.messaging import Messaging 4 | from protocol.messageFactory import MessageFactory 5 | 6 | class Connection: 7 | def __init__(self, sock): 8 | self.socket = sock 9 | self.messaging = Messaging(self) 10 | 11 | def send(self, buffer): 12 | self.socket.send(buffer) 13 | 14 | def receive_message(self): 15 | try: 16 | header = self.socket.recv(7) 17 | if len(header) > 0: 18 | message_type, length, version = Messaging.readHeader(header) 19 | payload = self.socket.recv(length) 20 | print(f"received message {message_type}, length {length}, version {version}") 21 | message = MessageFactory.create_message_by_type(message_type) 22 | if message != None: 23 | message.stream.buffer = payload 24 | message.decode() 25 | message.process(self) 26 | return 0 27 | except: 28 | return -1 -------------------------------------------------------------------------------- /network/messaging.py: -------------------------------------------------------------------------------- 1 | from protocol.piranhamessage import PiranhaMessage 2 | 3 | class Messaging: 4 | def __init__(self, con): 5 | self.connection = con 6 | 7 | def send_message(self, message : PiranhaMessage): 8 | if len(message.stream.buffer) == 0: 9 | message.encode() 10 | header = Messaging.writeHeader(message.id, len(message.stream.buffer), message.version) 11 | self.connection.send(header + message.stream.buffer) 12 | print(f"sent message with id {message.id}, length: {len(message.stream.buffer)}") 13 | 14 | @staticmethod 15 | def writeHeader(type, length, version): 16 | buffer = b'' 17 | buffer += int.to_bytes(type, 2, "big") 18 | buffer += int.to_bytes(length, 3, "big") 19 | buffer += int.to_bytes(version, 2, "big") 20 | return buffer 21 | 22 | @staticmethod 23 | def readHeader(buffer): 24 | return int.from_bytes(buffer[:2], "big"), int.from_bytes(buffer[2:5], "big"), int.from_bytes(buffer[5:], "big") -------------------------------------------------------------------------------- /network/tcpserver.py: -------------------------------------------------------------------------------- 1 | import socket, select 2 | from network.connection import Connection 3 | 4 | class TcpServer: 5 | def __init__(self, addr): 6 | self.addr = addr 7 | self.socket = socket.socket() 8 | self.socket.setblocking(0) 9 | self.inputs = {self.socket: None} 10 | 11 | def disconnect(self, socket): 12 | if socket in self.inputs: 13 | print("disconnect!") 14 | socket.close() 15 | self.inputs.pop(socket) 16 | 17 | def start_accept(self): 18 | self.socket.bind(self.addr) 19 | self.socket.listen() 20 | print("TCP server started!") 21 | while True: 22 | read_fds, write_fds, except_fds = select.select(self.inputs, [], self.inputs) # [] - output 23 | for i in read_fds: 24 | if i == self.socket: 25 | client, addr = self.socket.accept() 26 | client.setblocking(0) 27 | print(f"new connection from {addr[0]}:{addr[1]}") 28 | self.inputs[client] = Connection(client) 29 | else: 30 | if i in self.inputs: 31 | try: 32 | result = self.inputs[client].receive_message() 33 | if result == -1: 34 | self.disconnect(i) 35 | except: self.disconnect(i) -------------------------------------------------------------------------------- /protocol/messageFactory.py: -------------------------------------------------------------------------------- 1 | from protocol.messages.clientHello import ClientHelloMessage 2 | 3 | packets = { 4 | 10100: ClientHelloMessage 5 | } 6 | 7 | class MessageFactory: 8 | def create_message_by_type(m_type): 9 | if m_type in packets: 10 | return packets[m_type]() 11 | return None -------------------------------------------------------------------------------- /protocol/messages/clientHello.py: -------------------------------------------------------------------------------- 1 | from protocol.piranhamessage import PiranhaMessage 2 | from protocol.messages.serverHello import ServerHelloMessage 3 | 4 | class ClientHelloMessage(PiranhaMessage): 5 | def __init__(self): 6 | super().__init__() 7 | self.id = 10100 8 | 9 | def decode(self): 10 | pass # decode 11 | 12 | def process(self, con): 13 | con.messaging.send_message(ServerHelloMessage()) -------------------------------------------------------------------------------- /protocol/messages/serverHello.py: -------------------------------------------------------------------------------- 1 | from protocol.piranhamessage import PiranhaMessage 2 | 3 | class ServerHelloMessage(PiranhaMessage): 4 | def __init__(self): 5 | super().__init__() 6 | self.id = 20100 7 | 8 | def encode(self): 9 | self.stream.writeInt(24) # session key 10 | for i in range(24): 11 | self.stream.writeByte(1) -------------------------------------------------------------------------------- /protocol/piranhamessage.py: -------------------------------------------------------------------------------- 1 | 2 | from datastream.bytestream import ByteStream 3 | 4 | class PiranhaMessage: 5 | def __init__(self): 6 | self.id = 0 7 | self.version = 0 8 | self.stream = ByteStream() 9 | 10 | def encode(self): pass 11 | def decode(self): pass 12 | def process(self, con): pass 13 | -------------------------------------------------------------------------------- /screens/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RomashkaTea/brawlserver-core/183afa76415ec9ae23b9f9788044c62c4fa6503f/screens/console.png --------------------------------------------------------------------------------