Current Godobuf master branch used for Godot version 4.0.
If you want use Godobuf plugin for other Godot versions see:
Godobuf is a Google Protocol buffers compiler from .proto-file description to GDScript output file.
Output file contains user protobuf messages represented by classes and protobuf core code which responsible for data serialization/deserialization.
Godobuf used as plugin for Godot v4.0
Godobuf is easy to use, does not require rebuilding the Godot, because it is written in GDScript. All you need is to start the plugin.
packed
for fieldsAny
typesaddons
directory from Godobuf repository to your Gotot project directory.Project->Project Settings
.Project Settings
window choose Plugins
tab.Active
.Project Settings
window.Input protobuf file
.Output GDScript file
.Compile
button.Output
for details.Output
godot --headless -s addons/protobuf/protobuf_cmdln.gd --input=A.proto --output=my_proto.gd
alias godobuf='godot -s addons/protobuf/protobuf_cmdln.gd'
Run unit tests (proto 2)
or Run unit tests (proto 3)
button to start the tests.Output
for details.Output
Protobuf | GDScript | GDScript typeof |
---|---|---|
int32, uint32, sint32, fixed32, sfixed32 int64, uint64, sint64, fixed64, sfixed64 |
int | TYPE_INT |
float | double / real note: number after unpacking will be single precision |
TYPE_REAL |
double | double / real | TYPE_REAL |
bool | bool | TYPE_BOOL |
enum | enum / int | TYPE_INT |
string | String | TYPE_STRING |
bytes | PoolByteArray | TYPE_RAW_ARRAY |
oneof | fields described in oneof note: fields are not grouped into a structure, but you can set the value for only one field |
different |
map | Dictionary | TYPE_DICTIONARY |
message | class | TYPE_OBJECT |
repeated fields | Array | TYPE_ARRAY |
Initially all fields setted as null.
Default values in .proto syntax not supported.
Repeated fields are setted as empty Array.
Initially all fields setted to default values specified in Protobuf manual.
Repeated fields are setted as empty Array.
Keywords cannot be used in any other constructions for which they are not intended. In particular, keywords cannot be used as field names and message names.
message, double, float, int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, string, bytes, enum, map, oneof, true, false, syntax, import, package, option, service, reserved, weak, public, repeated, required, optional, allow_alias, custom_option, extensions, packed
const MyProto = preload("res://my_proto.gd")
syntax = "proto3";
message A {
double f1 = 1;
B f2 = 2;
}
Packed message must be instanced as GDScript class object directly.
Pack message A:
# Create packed class (message)
var a = MyProto.A.new()
# Example, set field f1
a.set_f1(12.554)
# Pack message A
# Use to_bytes() method, it's return PoolByteArray
var packed_bytes = a.to_bytes()
Unpacked message must be instanced as GDScript class object directly.
Re-calling from_bytes() of the same object instance is not allowed. You must create new object instance.
Unpack message A:
# Create unpacked class (message)
var a = MyProto.A.new()
# Unpack byte sequence to class (message) A.
# Use from_bytes(PoolByteArray my_byte_sequence) method
var result_code = a.from_bytes(my_byte_sequence)
# result_code must be checked (see Unpack result codes section)
if result_code == MyProto.PB_ERR.NO_ERRORS:
print("OK")
else:
return
# Use class 'a' fields. Example, get field f1
var f1 = a.get_f1()
from_bytes() method returns a success or an error state.
User must check the result code to make sure the unpacking is correct (see 'Unpack message' section above).
Result codes (enum PB_ERR) are presented in table below.
Unpack result code | int representation | Description |
---|---|---|
NO_ERRORS | 0 | Success |
VARINT_NOT_FOUND | -1 | Parse error. Byte sequence does not contains varint attribute, but varint is described in .proto file. |
REPEATED_COUNT_NOT_FOUND | -2 | Parse error. Byte sequence contains negative size of repeated block. |
REPEATED_COUNT_MISMATCH | -3 | Parse error. Byte sequence size less than field type size or block size. |
LENGTHDEL_SIZE_NOT_FOUND | -4 | Parse error. Byte sequence contains negative size of length delimited field type. |
LENGTHDEL_SIZE_MISMATCH | -5 | Parse error. Byte sequence size less than length delimited field type size. |
PACKAGE_SIZE_MISMATCH | -6 | Parse error. Byte sequence size less than package required length. |
UNDEFINED_STATE | -7 | Error. Unspecified error. |
PARSE_INCOMPLETE | -8 | Byte sequence error. Byte sequence is correct and corresponds to the .proto descriptor, but it's size is incomplete. The package (byte sequence) did not come completely. |
REQUIRED_FIELDS | -9 | Sender error. For Protobuf v2. Byte sequence is correct and corresponds to the .proto descriptor, but not all required fields are filled (data is missing in the byte sequence). |
Scalar types: int32, uint32, sint32, fixed32, sfixed32, int64, uint64, sint64, fixed64, sfixed64, float, double, bool.
Use method set_<field_name>(value)
. Sets field value.
Use method get_<field_name>()
. Returns field value.
.proto-file
syntax = "proto3"; message A { double f1 = 1; int32 f2 = 2; }
GDScript. Using set
var a = MyProto.A.new() a.set_f1(12.554) a.set_f2(500)
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_field_f1 = a.get_f1() var my_field_f2 = a.get_f2()
Use similarly to scalar types.
Use method set_<field_name>(value)
. Sets field value.
Use method get_<field_name>()
. Returns field value.
.proto-file
syntax = "proto3"; message A { string f1 = 1; }
GDScript. Using set
var a = MyProto.A.new() a.set_f1("my string")
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_field_f1 = a.get_f1()
Use similarly to scalar types.
Use method set_<field_name>(value)
. Sets field value.
Use method get_<field_name>()
. Returns field value as PoolByteArray.
.proto-file
syntax = "proto3"; message A { bytes f1 = 1; bytes f2 = 2; }
GDScript. Using set
var a = MyProto.A.new() # Correct both a.set_f1([1,2,3,4,5]) a.set_f2(PoolByteArray([0,3,4,5,7]))
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_pool_byte_array_f1 = a.get_f1()
Use similarly to scalar types, but values should be taken from Enum which was generated by compiler.
Generated Enums are named according to the following rule:
<message root>.<message inner>. ... <enum name>
Using enum name in GDScript:
<preloaded/loaded resource instance name>.<class root>.<class inner>. ... <enum name>
Use method set_<field_name>(value)
. Sets field value.
Use method get_<field_name>()
. Returns field value.
.proto-file
syntax = "proto3"; enum TestEnum { VALUE_0 = 0; VALUE_1 = 1; VALUE_2 = 2; }
message B { enum BEnum { BVALUE_0 = 0; BVALUE_1 = 1; BVALUE_2 = 2; } }
message A { TestEnum f1 = 1; B.BEnum f2 = 2; }
> GDScript. Using set
```gdscript
var a = MyProto.A.new()
a.set_f1(MyProto.TestEnum.VALUE_1)
a.set_f2(MyProto.B.BEnum.BVALUE_2)
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_field_f1 = a.get_f1() if my_field_f1 == MyProto.TestEnum.VALUE_1: print("OK value-1")
Use similarly to scalar types, but you can set the value for only one field grouped in oneof.
Oneof group name not used in GDScript.
If you set one field of oneof group another fields will be cleared automatically.
Use method set_<field_name>(value)
. Sets field value.
Use method get_<field_name>()
. Returns field value.
Use method has_<field_name>()
. Returns true if value is been setted in byte_sequence.
.proto-file
syntax = "proto3"; message A { oneof my_oneof { string f1 = 1; int32 f2 = 2; map<int32, int32> f3 = 3; } }
GDScript. Using set
var a = MyProto.A.new() a.set_f1("my string") # if you set f2: f1 and f3 will be setted to default values (or null for Protobuf v2) a.set_f2(10)
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_field_f1 = a.get_f1()
GDScript. Using has
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... if a.has_f1(): print("F1") elif a.has_f2(): print("F2")
For non-message map values used method:
add_<field_name>(key, value)
. Append one key-value pair to Dictionary (map) field.
If map value is message (class) used method:
add_<field_name>(key)
. Append one key-value pair to Dictionary (map) field and return value object instance.
Use method get_<field_name>()
. Returns GDScript Dictionary (map).
.proto-file
syntax = "proto3"; message A { message B { int32 f1 = 1; int32 f2 = 2; } map<int32, string> f1 = 1; map<int32, B> f2 = 2; }
GDScript. Using add
var a = MyProto.A.new() a.add_f1(1, "one") a.add_f1(2, "two") a.add_f1(4, "four")
var b b = a.add_f2(10) b.set_f1(100) b.set_f2(200) b = a.add_f2(20) b.set_f1(1000) b.set_f2(2000)
> GDScript. Using get
```gdscript
var a = MyProto.A.new()
var state = a.from_bytes(byte_sequence)
# First you have to check the 'state' ...
var my_dict_f1 = a.get_f1()
Use method new_<field_name>()
. Create object instance, sets as field value and returns instance.
Use method get_<field_name>()
. Returns object instance.
.proto-file
syntax = "proto3"; message A { message B { int64 Bf1 = 1; float Bf2 = 2; } message C { string Cf1 = 1; B Cf2 = 2; }
int32 Af1 = 1;
C Af2 = 2;
}
> GDScript. Using new (set)
```gdscript
var a = MyProto.A.new()
a.set_Af1(10)
var c = a.new_Af2()
c.set_Cf1("my string")
var b = c.new_Cf2()
b.set_Bf1(20)
b.set_Bf2(2.5)
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_class_instance_c = a.get_Af2() var my_int32 = my_class_instance_c.get_Af1() # ...
Use method add_<field_name>(value)
. Append value to Array.
Use method add_<field_name>()
. Create object instance, sets as next Array element and returns instance.
Use method get_<field_name>()
. Returns Array of fields.
.proto-file
syntax = "proto3"; message A { message B { int64 Bf1 = 1; float Bf2 = 2; }
repeated int32 Af1 = 1;
repeated B Af2 = 2;
}
> GDScript. Using add
```gdscript
var a = MyProto.A.new()
a.add_Af1(10)
a.add_Af1(20)
a.add_Af1(30)
var b
b = a.add_Af2()
b.set_Bf1(100)
b.set_Bf2(1.5)
b = a.add_Af2()
b.set_Bf1(200)
b.set_Bf2(2.5)
GDScript. Using get
var a = MyProto.A.new() var state = a.from_bytes(byte_sequence) # First you have to check the 'state' ... var my_array_b = a.get_Af2() for inst in my_array_b: print(inst.get_Bf1())
All message classes have to_string() method.
to_sting() generate debug string, which contains field names and their values. Default values are ignored.
field_<type>
- field name
field_int: 12;
field_float: 12.34;
field_bool: True;
field_string: "test";
field_enum: ENUM::2;
field_message_1: {
field_message_2: {
field_int: 12;
field_float: 12.34;
}
field_bool: True;
field_string: "test";
};
field_repeated: [
0: {
field_int: 123;
field_bool: True;
},
1: {
field_int: 454;
field_bool: False;
}
];
field_repeated: [
1: 2,
2: 3,
3: 4
];
field_map: (
"key1": "value1",
"key2": "value2"
);
field_map: (
"object1": {
field_int: 123;
field_bool: True;
},
"object2": {
field_int: 454;
field_bool: False;
}
);
field_bytes: <1, 2, 4, 5>;
field_empty_message: {};
# Debug print, where "message" - any Protobuf class (message)
print(message.to_string())
Google Protobuf: https://developers.google.com/protocol-buffers/docs/overview
Godot engine: http://godotengine.org
Godot docs: https://docs.godotengine.org/en/stable/
Godobuf test suites: https://github.com/kittenseater/godobuf-test
https://github.com/kittenseater/godobuf-gen-v2
https://github.com/kittenseater/godobuf-gen-v3