Last active
September 14, 2022 15:11
-
-
Save rickwillcox/419cd5605cb438ec474c35fc5accf1b6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extends Node | |
var si = ServerInterface | |
class_name Serializer | |
# Packet description from server to client. Default type is ENTITY_ID | |
static func get_server_client_descriptor(): | |
var server_to_client_packet_descriptor = { | |
ServerInterface.Opcodes.TAKE_DAMAGE : | |
PacketBundle.create_packet_descriptor([["attacker"], ["victim"], ["damage"]]), | |
ServerInterface.Opcodes.INVENTORY_OK : [], | |
ServerInterface.Opcodes.INVENTORY_NOK : [], | |
ServerInterface.Opcodes.REMOVE_ITEM : | |
PacketBundle.create_packet_descriptor([["item_id"]]), | |
ServerInterface.Opcodes.ATTACK_SWING : | |
PacketBundle.create_packet_descriptor([["attacker"], ["victim"]]), | |
ServerInterface.Opcodes.PLAYER_SPAWN : | |
PacketBundle.create_packet_descriptor([["player_id"], | |
["position", PacketBundle.FieldType.VECTOR2], | |
["username", PacketBundle.FieldType.SHORT_STRING]]), | |
ServerInterface.Opcodes.PLAYER_INITIAL_INVENTORY: | |
PacketBundle.create_packet_descriptor([["player_id"], | |
["head"], | |
["chest"], | |
["legs"], | |
["feet"], | |
["hands"]]), | |
ServerInterface.Opcodes.PLAYER_DESPAWN : | |
PacketBundle.create_packet_descriptor([["player_id"]]), | |
ServerInterface.Opcodes.PLAYER_DEAD : | |
PacketBundle.create_packet_descriptor([ | |
["player_id"], | |
["player_position", PacketBundle.FieldType.VECTOR2]]), | |
ServerInterface.Opcodes.PLAYER_INVENTORY_UPDATE : | |
PacketBundle.create_packet_descriptor([["player_id"], | |
["slot", PacketBundle.FieldType.INT8], | |
["item_id"]]), | |
ServerInterface.Opcodes.CHAT : | |
PacketBundle.create_packet_descriptor([["player_id"], | |
["content", PacketBundle.FieldType.SHORT_STRING]]), | |
ServerInterface.Opcodes.ENEMY_SPAWN: | |
PacketBundle.create_packet_descriptor([["enemy_id"], | |
["enemy_state", PacketBundle.FieldType.INT8], | |
["enemy_type", PacketBundle.FieldType.INT8], | |
["health"], | |
["position", PacketBundle.FieldType.VECTOR2]]), | |
ServerInterface.Opcodes.ENEMY_DIED: | |
PacketBundle.create_packet_descriptor([["enemy_id"]]), | |
ServerInterface.Opcodes.ENEMY_DESPAWN: | |
PacketBundle.create_packet_descriptor([["enemy_id"]]), | |
ServerInterface.Opcodes.ITEM_CRAFT_OK: | |
PacketBundle.create_packet_descriptor([["slot", PacketBundle.FieldType.INT8], | |
["item_id"]]), | |
ServerInterface.Opcodes.ITEM_CRAFT_NOK: [], | |
ServerInterface.Opcodes.SMELTER_STARTED: [], | |
ServerInterface.Opcodes.SMELTER_STOPPED: [], | |
ServerInterface.Opcodes.INVENTORY_SLOT_UPDATE: | |
PacketBundle.create_packet_descriptor([ | |
["slot", PacketBundle.FieldType.INT8], | |
["item_id"], | |
["amount", PacketBundle.FieldType.INT8]]), | |
} | |
return server_to_client_packet_descriptor | |
const INT8_SIZE = 1 | |
const INT64_SIZE = 8 | |
class PacketBundle extends Object: | |
const COMPRESSION_MODE = 1 | |
var buffer : PoolByteArray | |
var itr = 0 | |
# this allows automatic serialization/descerialization | |
enum FieldType {INT8 = 0, INT16, INT32, INT64 = 3, ENTITY_ID = 3, FLOAT, VECTOR2, SHORT_STRING, LONG_STRING} | |
func _serialize_packet(data, op_code, input_packet_descriptor): | |
var descriptor = input_packet_descriptor[op_code] | |
for field in descriptor: | |
var field_name = field["name"] | |
match field["type"]: | |
FieldType.INT8: | |
serialize_int_8(data[field_name]) | |
FieldType.INT64: | |
serialize_int_64(data[field_name]) | |
FieldType.FLOAT: | |
serialize_float(data[field_name]) | |
FieldType.SHORT_STRING: | |
serialize_string(data[field_name]) | |
FieldType.VECTOR2: | |
serialize_vec2(data[field_name]) | |
_: | |
assert(false) | |
func _deserialize_packet(op_code, output_packet_descriptor) -> Dictionary: | |
var descriptor = output_packet_descriptor[op_code] | |
var data = { "op_code" : op_code } | |
for field in descriptor: | |
var field_name = field["name"] | |
match field["type"]: | |
FieldType.INT8: | |
data[field_name] = deserialize_int_8() | |
FieldType.INT64: | |
data[field_name] = deserialize_int_64() | |
FieldType.FLOAT: | |
data[field_name] = deserialize_float() | |
FieldType.VECTOR2: | |
data[field_name] = deserialize_vec2() | |
FieldType.SHORT_STRING: | |
data[field_name] = deserialize_string() | |
_: | |
assert(false) | |
return data | |
func _init(): | |
buffer = PoolByteArray() | |
func serialize_int_64(x : int): | |
for i in range(INT64_SIZE): | |
buffer.append((x >> (8 * i)) & 0xff) | |
# max length 255 | |
func serialize_string(x : String): | |
var x_utf = x.to_utf8() | |
serialize_int_8(x_utf.size()) | |
buffer.append_array(x_utf) | |
func serialize_float(x : float): | |
# multiply x by power of 2. later when its cast to int | |
# we retain some of the decimal places | |
var x_int = int(x * pow(2, 10)) | |
serialize_int_64(x_int) | |
func deserialize_float(): | |
var x_int = deserialize_int_64() | |
return float(x_int) / pow(2, 10) | |
func serialize_vec2(x : Vector2): | |
serialize_float(x.x) | |
serialize_float(x.y) | |
func deserialize_vec2(): | |
var x = deserialize_float() | |
var y = deserialize_float() | |
return Vector2(x, y) | |
func compress(): | |
buffer = buffer.compress(COMPRESSION_MODE) | |
func decompress(original_size : int): | |
var new_buffer = buffer.decompress(original_size, COMPRESSION_MODE) | |
buffer = new_buffer | |
func serialize_int_8(x : int): | |
buffer.append(x & 0xff) | |
func deserialize_int_8(): | |
var res = int(buffer[itr]) | |
itr += 1 | |
return res | |
func deserialize_int_64() -> int: | |
var res = 0 | |
for i in range(INT64_SIZE): | |
res += buffer[itr + i] << (i * INT64_SIZE) | |
itr += INT64_SIZE | |
return res | |
func deserialize_string() -> String: | |
var string_length = deserialize_int_8() | |
var content = buffer.subarray(itr, itr+string_length -1).get_string_from_utf8() | |
itr += string_length | |
return content | |
func serialize_packet_into_bundle(data, packet_descriptor = {}): | |
# Opcodes that dont have payload dont need to be serialized further | |
var op_code = data["op_code"] | |
serialize_int_8(data["op_code"]) | |
match op_code: | |
# space for custom packet serialization | |
_: | |
_serialize_packet(data, op_code, packet_descriptor) | |
func serialize_packets(packets : Array, packet_descriptor): | |
for packet in packets: | |
serialize_packet_into_bundle(packet, packet_descriptor) | |
func deserialize_packet_from_bundle(packet_descriptor = {}) -> Dictionary: | |
if (itr + INT8_SIZE) > buffer.size(): | |
return {} | |
var op_code = deserialize_int_8() | |
var data | |
match op_code: | |
# space for custom packet deserialization | |
_: | |
data = _deserialize_packet(op_code, packet_descriptor) | |
data["op_code"] = op_code | |
return data | |
func deserialize_packets(packet_descriptor) -> Array: | |
var packets = [] | |
var has_packets = true | |
while has_packets: | |
var res = deserialize_packet_from_bundle(packet_descriptor) | |
if res.empty(): | |
has_packets = false | |
break | |
packets.append(res) | |
return packets | |
static func create_packet_descriptor(fields = []): | |
var field_dicts = [] | |
for field in fields: | |
var type = PacketBundle.FieldType.ENTITY_ID | |
if ((field as Array).size() > 1): | |
type = field[1] | |
field_dicts.append({"name" : field[0], "type":type}) | |
return field_dicts |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment