New protocol.hjson type system

This commit is contained in:
DankParrot 2020-08-18 18:26:46 -07:00
parent e71ae319a2
commit e09c70a51a
2 changed files with 151 additions and 76 deletions

View File

@ -5,6 +5,10 @@ text = ''
varint_max_size = 5 varint_max_size = 5
indent_count = 0 indent_count = 0
# Print to stderr
def warn(value, *args, sep='', end='\n', flush=False):
print(value, *args, sep=sep, end=end, file=sys.stderr, flush=flush)
def indent(): def indent():
global indent_count global indent_count
indent_count += 1 indent_count += 1
@ -32,37 +36,74 @@ def extract_array_type(type):
def extract_array_count(type): def extract_array_count(type):
if '[' in type and ']' in type: if '[' in type and ']' in type:
return int(type[type.find('[')+1:type.rfind(']')]) return int(type[type.find('[')+1:type.rfind(']')])
return 1 return None
def resolve_type(aliases, type): def get_type_info(types, typename, aliases={}):
type_no_array = type.rstrip('_') name = extract_array_type(typename)
if type_no_array in aliases:
return aliases[type_no_array]
return type
def get_type_size(type): alias_name = None
global varint_max_size if aliases and name in aliases:
count = extract_array_count(type) alias_name = name
type = extract_array_type(type) name = aliases[name]
# TODO: encode type sizes in Hjson data if not name in types:
if type == 'varint': warn('WARNING: Type name "{}" is not a known type or alias.'.format(name))
return varint_max_size return {
elif type == 'int64' or type == 'uint64' or type == 'double' or type == 'position': type: name
return 8 }
elif type == 'int32' or type == 'uint32' or type == 'float':
return 4 tp = types[name]
elif type == 'int16' or type == 'uint16':
return 2 if 'alias' in tp:
elif type == 'int8' or type == 'uint8' or type == 'byte' or type == 'bool': alias = tp['alias']
return 1 tp = get_type_info(types, tp['alias']).copy()
elif type == 'string': tp['alias'] = alias
return count if tp['generic'] == True:
elif type == 'uuid': tp['type'] = name
return 16
if alias_name:
tp = tp.copy()
tp['alias'] = name
tp['type'] = alias_name
# types without a 'type' value use their own name (e.g. double, float)
if not 'type' in tp:
tp['type'] = name
return tp
def resolve_type(types, typename, aliases={}):
tp = get_type_info(types, typename, aliases)
if 'type' in tp:
return tp['type']
else: else:
print(type) return typename
assert False
def get_type_size(types, typename, aliases={}):
global varint_max_size
tp = get_type_info(types, typename, aliases)
if not 'size' in tp:
if 'alias' in tp:
return get_type_size(types, tp['alias'])
else:
warn('ERROR: Non-alias type "{}" does not have "size" field.'.format(typename))
exit(1)
size = tp['size']
if size == 'count':
size = extract_array_count(typename)
if not size:
warn('ERROR: Cannot get size for type "{}"'.format(typename))
exit(1)
#print("get_type_size: {} -> {}".format(typename, size))
return int(size)
def print_enum(name, dict, primitive = 'int32_t'): def print_enum(name, dict, primitive = 'int32_t'):
add_text('enum class {} : {}', name, primitive) add_text('enum class {} : {}', name, primitive)
@ -73,21 +114,27 @@ def print_enum(name, dict, primitive = 'int32_t'):
unindent() unindent()
add_text('}};') add_text('}};')
def get_rw_func(primitiveType, aliasedType, read): def get_rw_func(types, typename, read, aliases={}):
prefix = 'Read' if read else 'Write' method = 'Read' if read else 'Write'
if aliasedType == 'varint':
if primitiveType == 'int32_t':
return '{}VarInt'.format(prefix)
else:
return '{}VarInt<{}>'.format(prefix, primitiveType)
elif aliasedType == 'string':
return '{}String'.format(prefix)
elif aliasedType == 'position':
return '{}Position'.format(prefix)
else:
return '{}<{}>'.format(prefix, primitiveType)
def print_messages(list, global_aliases, primitives): #print("{}: ({}, {})".format(method, typename, read))
tp = get_type_info(types, typename, aliases)
if 'method' in tp:
method += tp['method']
generic = 'generic' in tp and tp['generic']
alias = 'alias' in tp
if (generic and alias) or not 'method' in tp:
method += '<' + tp['type'] + '>'
#print('{} -> {}'.format(typename, method))
return method
def print_messages(list, types):
global text global text
for state, direction_list in list.items(): for state, direction_list in list.items():
add_text('namespace {}', state.capitalize()) add_text('namespace {}', state.capitalize())
@ -97,10 +144,8 @@ def print_messages(list, global_aliases, primitives):
serverbound = direction == 'serverbound' serverbound = direction == 'serverbound'
for message_name, message in messages.items(): for message_name, message in messages.items():
# global and local aliases
aliases = global_aliases.copy()
# add any local aliases # add any local aliases
aliases = {}
if 'aliases' in message: if 'aliases' in message:
for alias in message['aliases']: for alias in message['aliases']:
aliases[alias] = message['aliases'][alias] aliases[alias] = message['aliases'][alias]
@ -108,8 +153,8 @@ def print_messages(list, global_aliases, primitives):
global varint_max_size global varint_max_size
size = varint_max_size # Packet Length size = varint_max_size # Packet Length
size += varint_max_size # Packet Id size += varint_max_size # Packet Id
for name, type in message['vars'].items(): for name, typename in message['vars'].items():
size += get_type_size(resolve_type(aliases, type)) size += get_type_size(types, typename, aliases)
struct_name = '{}{}'.format(direction.capitalize(), message_name) struct_name = '{}{}'.format(direction.capitalize(), message_name)
add_text('struct {}', struct_name) add_text('struct {}', struct_name)
@ -124,7 +169,7 @@ def print_messages(list, global_aliases, primitives):
for enum in message['enums']: for enum in message['enums']:
if enum in aliases: if enum in aliases:
# lookup enum primitive type from aliases # lookup enum primitive type from aliases
prim = resolve_type(primitives, aliases[enum]) prim = resolve_type(types, aliases[enum])
print_enum(enum, message['enums'][enum], prim) print_enum(enum, message['enums'][enum], prim)
else: else:
print_enum(enum, message['enums'][enum]) print_enum(enum, message['enums'][enum])
@ -133,8 +178,11 @@ def print_messages(list, global_aliases, primitives):
add_text('{}(PacketReader& reader)', struct_name) add_text('{}(PacketReader& reader)', struct_name)
add_text('{{') add_text('{{')
indent() indent()
for name, type in message['vars'].items(): for name, typename in message['vars'].items():
add_text('{} = reader.{}();', name, get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), True)) add_text('{} = reader.{}();',
name,
get_rw_func(types, typename, True, aliases)
)
unindent() unindent()
add_text('}}') add_text('}}')
newline() newline()
@ -144,15 +192,18 @@ def print_messages(list, global_aliases, primitives):
indent() indent()
add_text('NetworkMessage msg(MaxSize);') add_text('NetworkMessage msg(MaxSize);')
add_text('msg.WriteVarInt(PacketId);') add_text('msg.WriteVarInt(PacketId);')
for name, type in message['vars'].items(): for name, typename in message['vars'].items():
add_text('msg.{}({});', get_rw_func(resolve_type(primitives, extract_array_type(type)), resolve_type(aliases, extract_array_type(type)), False), name) add_text('msg.{}({});',
get_rw_func(types, typename, False, aliases),
name
)
add_text('msg.Finalize();') add_text('msg.Finalize();')
add_text('return msg;') add_text('return msg;')
unindent() unindent()
add_text('}}') add_text('}}')
for name, type in message['vars'].items(): for name, typename in message['vars'].items():
resolved_type = resolve_type(primitives, extract_array_type(type)) resolved_type = resolve_type(types, extract_array_type(typename), aliases)
if not serverbound and resolved_type == 'std::string': if not serverbound and resolved_type == 'std::string':
add_text('const {}& {};', resolved_type, name) add_text('const {}& {};', resolved_type, name)
else: else:
@ -229,8 +280,8 @@ def print_protocol():
print_messages( print_messages(
message_scheme['messages'], message_scheme['messages'],
message_scheme['types']['aliases'], message_scheme['types']
message_scheme['types']['primitives']) )
newline() newline()

View File

@ -1,29 +1,53 @@
{ {
types : types:
{ {
aliases : varint: {
{ type: int32_t
ProtocolState : varint size: 5
method: VarInt
generic: true
} }
primitives : ProtocolState: {
{ alias: varint
varint : int32_t
string : std::string
uuid : MinecraftUUID
position: BlockPos
uint64 : uint64_t
int64 : int64_t
uint32 : uint32_t
int32 : int32_t
uint16 : uint16_t
int16 : int16_t
uint8 : uint8_t
int8 : int8_t
byte : uint8_t
bool : uint8_t
} }
string: {
type: std::string
method: String
size: count
}
uuid: {
type: MinecraftUUID
size: 16
}
position: {
type: BlockPos
size: 8
method: Position
}
uint64: {type: 'uint64_t', size: 8}
int64: {type: 'int64_t', size: 8}
uint32: {type: 'uint32_t', size: 4}
int32: {type: 'int32_t', size: 4}
uint16: {type: 'uint16_t', size: 2}
int16: {type: 'int16_t', size: 2}
uint8: {type: 'uint8_t', size: 1}
int8: {type: 'int8_t', size: 1}
byte: {type: 'uint8_t', size: 1}
bool: {type: 'bool', size: 1}
float: {size: 4}
double: {size: 8}
} }
states : states :