New protocol.hjson type system
This commit is contained in:
parent
e71ae319a2
commit
e09c70a51a
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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 :
|
||||||
|
|
Loading…
Reference in New Issue