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
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():
global indent_count
indent_count += 1
@ -32,37 +36,74 @@ def extract_array_type(type):
def extract_array_count(type):
if '[' in type and ']' in type:
return int(type[type.find('[')+1:type.rfind(']')])
return 1
return None
def resolve_type(aliases, type):
type_no_array = type.rstrip('_')
if type_no_array in aliases:
return aliases[type_no_array]
return type
def get_type_info(types, typename, aliases={}):
name = extract_array_type(typename)
def get_type_size(type):
global varint_max_size
count = extract_array_count(type)
type = extract_array_type(type)
alias_name = None
if aliases and name in aliases:
alias_name = name
name = aliases[name]
# TODO: encode type sizes in Hjson data
if type == 'varint':
return varint_max_size
elif type == 'int64' or type == 'uint64' or type == 'double' or type == 'position':
return 8
elif type == 'int32' or type == 'uint32' or type == 'float':
return 4
elif type == 'int16' or type == 'uint16':
return 2
elif type == 'int8' or type == 'uint8' or type == 'byte' or type == 'bool':
return 1
elif type == 'string':
return count
elif type == 'uuid':
return 16
if not name in types:
warn('WARNING: Type name "{}" is not a known type or alias.'.format(name))
return {
type: name
}
tp = types[name]
if 'alias' in tp:
alias = tp['alias']
tp = get_type_info(types, tp['alias']).copy()
tp['alias'] = alias
if tp['generic'] == True:
tp['type'] = name
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:
print(type)
assert False
return typename
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'):
add_text('enum class {} : {}', name, primitive)
@ -73,21 +114,27 @@ def print_enum(name, dict, primitive = 'int32_t'):
unindent()
add_text('}};')
def get_rw_func(primitiveType, aliasedType, read):
prefix = '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 get_rw_func(types, typename, read, aliases={}):
method = 'Read' if read else 'Write'
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
for state, direction_list in list.items():
add_text('namespace {}', state.capitalize())
@ -97,10 +144,8 @@ def print_messages(list, global_aliases, primitives):
serverbound = direction == 'serverbound'
for message_name, message in messages.items():
# global and local aliases
aliases = global_aliases.copy()
# add any local aliases
aliases = {}
if 'aliases' in message:
for alias in message['aliases']:
aliases[alias] = message['aliases'][alias]
@ -108,8 +153,8 @@ def print_messages(list, global_aliases, primitives):
global varint_max_size
size = varint_max_size # Packet Length
size += varint_max_size # Packet Id
for name, type in message['vars'].items():
size += get_type_size(resolve_type(aliases, type))
for name, typename in message['vars'].items():
size += get_type_size(types, typename, aliases)
struct_name = '{}{}'.format(direction.capitalize(), message_name)
add_text('struct {}', struct_name)
@ -124,7 +169,7 @@ def print_messages(list, global_aliases, primitives):
for enum in message['enums']:
if enum in 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)
else:
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('{{')
indent()
for name, type 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))
for name, typename in message['vars'].items():
add_text('{} = reader.{}();',
name,
get_rw_func(types, typename, True, aliases)
)
unindent()
add_text('}}')
newline()
@ -144,15 +192,18 @@ def print_messages(list, global_aliases, primitives):
indent()
add_text('NetworkMessage msg(MaxSize);')
add_text('msg.WriteVarInt(PacketId);')
for name, type 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)
for name, typename in message['vars'].items():
add_text('msg.{}({});',
get_rw_func(types, typename, False, aliases),
name
)
add_text('msg.Finalize();')
add_text('return msg;')
unindent()
add_text('}}')
for name, type in message['vars'].items():
resolved_type = resolve_type(primitives, extract_array_type(type))
for name, typename in message['vars'].items():
resolved_type = resolve_type(types, extract_array_type(typename), aliases)
if not serverbound and resolved_type == 'std::string':
add_text('const {}& {};', resolved_type, name)
else:
@ -229,8 +280,8 @@ def print_protocol():
print_messages(
message_scheme['messages'],
message_scheme['types']['aliases'],
message_scheme['types']['primitives'])
message_scheme['types']
)
newline()

View File

@ -1,29 +1,53 @@
{
types :
types:
{
aliases :
{
ProtocolState : varint
varint: {
type: int32_t
size: 5
method: VarInt
generic: true
}
primitives :
{
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
ProtocolState: {
alias: varint
}
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 :