Giter Site home page Giter Site logo

godobuf's Introduction

Godobuf v0.6.0

Changelog in v0.6.0

  • Added support for Godot 4
  • Fixed bug with return type in add method for map

What's new in v0.5.0

  • Implemented unit tests
  • Fixed repeat field clearing bug
  • Fixed map bug with default values

Version

Current Godobuf master branch used for Godot version 4.0.
If you want use Godobuf plugin for other Godot versions see:

About

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.

Features

Supported

  • Protobuf v2 and v3
  • Message definitions (cascade messages)
  • Simple Protobuf types (int32, uint32, sint32, fixed32, sfixed32
    int64, uint64, sint64, fixed64, sfixed64, float, double, bool, enum, string, bytes)
  • Oneof type
  • Map type
  • Repeated fields
  • Option packed for fields
  • Protobuf imports (also public modificator)
  • Debug string for class (message) fields (since v0.2.1)

Not supported

  • Packages
  • Reserved statements
  • Services
  • Any types
  • Default values option for fields

Installation

  1. Create a new project in Godot or use an existing project.
  2. Copy addons directory from Godobuf repository to your Gotot project directory.
  3. Choose in Menu Project->Project Settings.
    Project->Project Settings
  4. In Project Settings window choose Plugins tab.
  5. Find Godobuf plugin and set status Active.
    Plugins
  6. Close Project Settings window.
  7. The Godobuf panel will be displayed in the Godot.
    Plugins
  8. Please note that the Godobuf panel may not be visible, because located on the last tab.
    Plugins

Usage

From the User Interface

  1. Open file dialog window Input protobuf file.
  2. Choose *.proto file in a dialog window.
    Input protobuf file
  3. Open file dialog window Output GDScript file.
  4. Choose directory and enter output file name.
    Output GDScript file
  5. Press Compile button.
  6. See Godot Output for details.
  7. All possilble errors details are displayed in Godot Output
  8. After task completion you'll see alert window which report you about compilation result.
    Alert
  9. If compilation was successful a new GDScript will be created in a specified directory.
  10. Use script in you project.

From the Command Line

  1. From the root folder of your project, run godot --headless -s addons/protobuf/protobuf_cmdln.gd --input=A.proto --output=my_proto.gd
  2. Optionally, define an alias: alias godobuf='godot -s addons/protobuf/protobuf_cmdln.gd'

Unit tests

  1. This option is mainly for those who modify the code or want to check the stability of the version.
  2. Press Run unit tests (proto 2) or Run unit tests (proto 3) button to start the tests.
  3. See Godot Output for details.
  4. All possilble errors details are displayed in Godot Output
  5. After task completion you'll see alert window which report you about tests result.

Mapping of protocol buffers datatypes to GDScript

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

Default values

Protobuf version 2

Initially all fields setted as null.
Default values in .proto syntax not supported.
Repeated fields are setted as empty Array.

Protobuf version 3

Initially all fields setted to default values specified in Protobuf manual.
Repeated fields are setted as empty Array.

Keywords

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.

Keywords:

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

Using generated .gd file in Godot project

  1. Open the script in which Protobuf will be used.
  2. Preload previously generated script:
const MyProto = preload("res://my_proto.gd")
  1. Use previously described messages as GDScript classes. Example, we have .proto file:
syntax = "proto3";
message A {
	double f1 = 1;
	B f2 = 2;
}

Pack (serialization) message

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()

Unpack (deserialization) message

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()

Unpack result codes

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).

API

1. Scalar types

Scalar types: int32, uint32, sint32, fixed32, sfixed32, int64, uint64, sint64, fixed64, sfixed64, float, double, bool.

Set value

Use method set_<field_name>(value). Sets field value.

Get value

Use method get_<field_name>(). Returns field value.

Example

.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()

2. String

Use similarly to scalar types.

Set value

Use method set_<field_name>(value). Sets field value.

Get value

Use method get_<field_name>(). Returns field value.

Example

.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()

3. Bytes

Use similarly to scalar types.

Set value

Use method set_<field_name>(value). Sets field value.

Get value

Use method get_<field_name>(). Returns field value as PoolByteArray.

Example

.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()

4. Enum

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>

Set value

Use method set_<field_name>(value). Sets field value.

Get value

Use method get_<field_name>(). Returns field value.

Example

.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

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")

5. Oneof

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.

Set value

Use method set_<field_name>(value). Sets field value.

Get value

Use method get_<field_name>(). Returns field value.

Has value

Use method has_<field_name>(). Returns true if value is been setted in byte_sequence.

Example

.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")

6. Map

Add value

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.

Get value

Use method get_<field_name>(). Returns GDScript Dictionary (map).

Example

.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

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()

7. Message

Set value

Use method new_<field_name>(). Create object instance, sets as field value and returns instance.

Get value

Use method get_<field_name>(). Returns object instance.

Example

.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)

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()
# ...

8. Repeated

Add value for non-message types

Use method add_<field_name>(value). Append value to Array.

Add value for message fields

Use method add_<field_name>(). Create object instance, sets as next Array element and returns instance.

Get value

Use method get_<field_name>(). Returns Array of fields.

Example

.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

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())

9. Debug method to_string()

All message classes have to_string() method.
to_sting() generate debug string, which contains field names and their values. Default values are ignored.

String output format:

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: {};

Example

# Debug print, where "message" - any Protobuf class (message)
print(message.to_string())

Related links

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

godobuf's People

Contributors

aaronfranke avatar oniksan avatar stefanlobbenmeier avatar vmednis avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

godobuf's Issues

Allow deepcopy of messages

Following #9, consider the following example:

message GiantSlime {}
message PettySlime {}
message MagicOrc {}
message NormalOrc {}

message Slime {
    oneof type_of_slime {
        GiantSlime giant_slime = 1;
        PettySlime petty_slime = 2;
    }
}

message Orc {
    oneof type_of_orc {
        MagicOrc magic_orc = 1;
        NormalOrc normal_orc = 2;
    }
}

message Enemy {
    oneof type_of_enemy {
        Slime slime = 1;
        Orc orc = 2;
    }
}

If one has the following function in GD Script:

const MyProto = preload("res://proto/myproto.gd")
....
func SpawnOrc(spawn_orc:MyProto.Orc) -> MyProto.Enemy:
    var spawn_enemy:MyProto.Enemy = MyProto.Enemy.new()
    var orc:MyProto.Orc = spawn_enemy.new_orc()
    orc = spawn_orc
    return spawn_enemy

spawn_enemy will not contain a copy of spawn_orc, but will instead be empty. The user would have to call the individual set_ function for each property, but will encounter that for composite properties that are defined by messages (like the one in this example) they'll have to initialise a new object and "set" it with the data that comes from spawn_orc.

The API could provide a function to "deepcopy" one proto into another, thus avoiding the need of going each property one by one and setting it to the correct value. An example would be:

const MyProto = preload("res://proto/myproto.gd")
....
func SpawnOrc(spawn_orc:MyProto.Orc) -> MyProto.Enemy:
    var spawn_enemy:MyProto.Enemy = MyProto.Enemy.new()
    var orc:MyProto.Orc = spawn_enemy.new_orc()
    orc.deepcopy(spawn_orc)
    return spawn_enemy

Nested oneof fields?

Hi Oleg,

Thanks for creating the proto converter! :)

I have three proto files:

File 1:
message P2PEvent {
oneof event {
FutureEvent future_event = 1;
ImmediateEvent immediate_event = 2;
}
}

File 2:
message FutureEvent {
oneof event {
...
}
}

File 3:
message ImmediateEvent {
oneof event {
...
}
}

I get errors when compile the parent file (File 1), I wonder if this case is supported?

  1. Parsing:
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/P2PEvent.proto: parsing.
    UNRELEASED desc_package: 1, nesting: 0
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/FutureEvent.proto: parsing.
    UNRELEASED desc_package: 1, nesting: 0
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/ImmediateEvent.proto: parsing.
    UNRELEASED desc_package: 1, nesting: 0
  • Parsing completed successfully. *
  1. Semantic analysis:
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/P2PEvent.proto: analysis.
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/FutureEvent.proto: analysis.
    D:/Projects/Owlies_Project/BouncingDefender/BouncingDefender/GodotProject/Proto/ImmediateEvent.proto: analysis.
  • Semantic analysis completed successfully. *
  1. Output file creating:
    Perform full semantic analysis.
    Perform translation.
    res://addons/protobuf/parser.gd:1766 - Invalid get index '14' (on base: 'Array').
    res://addons/protobuf/parser.gd:1816 - Invalid operands 'String' and 'Nil' in operator '+'.
    res://addons/protobuf/parser.gd:1943 - Invalid operands 'String' and 'Nil' in operator '+'.
    res://addons/protobuf/parser.gd:2033 - Invalid operands 'String' and 'Nil' in operator '+'.

Appreciate the help!

feature: Prefix type names

GDScript doesn't support namespaces, and so I'm running into a situation where proto messages can't be called the same thing as GDScript types.

For example, a serialized "Enemy" message vs. a Godot node type named "Enemy".

It would be nice if in the Godobuf importer we could set a name prefix, for example "Proto", so message Enemy is translated to GDScript as ProtoEnemy.

C# Support

Where is C# or VisualScript support? I really need Protobuf for C# but Godot isn't recognizing it as a NuGet package.

Error parsing enums

v0.4.4 crashes whenever scanning enums. I even tried it with the README enum
Screen Shot 2021-09-18 at 3 30 43 PM
Screen Shot 2021-09-18 at 3 27 51 PM

Add support for Protobuf 3.15 optional fields

Protobuf 3.15 (Feb 18, 2021) introduced optional messages to know if a message value was set or not.

Example from: https://stackoverflow.com/a/62566052/12619313

syntax = "proto3";

message Foo {
    int32 bar = 1;
    optional int32 baz = 2;
}

A has_baz()/hasBaz() method is generated for the optional field above, just as it was in proto2.

This is useful for example if you want to display imported 3d OpenStreetMap data that only sometimes contains height data.

message Vec3 {
  float x = 1;
  optional float y = 2;  // Godot UP
  float z = 3;
}

Services and packages

The addon is amazing, is it still in development?
Are services and packages planned to come on next updates? If not, do you think it's possible? (In case I decide to fork and attempt to work on it)

oneof field with messages in it not generating set method

Hello,
first of a BIG thank you for all the work that went into this project!

I'm trying to have a message wrapper so I can send only one message over the wire in order to have easier message recognition on the receiving end.

syntax = "proto3";

enum PlayerCommand {
    SHOOT = 0;
}
message PlayerPosition {
    int32 id = 1;
    float x = 2;
    float y = 3;
    float rot = 4;
}

message PlayerInput {
    int32 id = 1;
    float rotL = 2;
    float rotM = 3;
    repeated PlayerCommand com = 4;
}

message PlayerPositions {
    repeated PlayerPosition playerpos = 1;
}

message CompositeMessage {
    oneof payload {
        PlayerPosition playerstart = 1;
        PlayerInput playerupdate = 2;
        int32 removeplayer = 3;
        PlayerPositions allplayersupdate = 4;
    }
  }

the generated code however does not seem to have the setter functions for playerstart, playerupdate and allplayersupdate. The only setter generated is set_removeplayer. The other type of functions like get_playerupdate , clear_playerupdate and new_playerupdate are generated correctly.

Namespaced nested imports not being found

All protobuf files are here:
https://github.com/OpenShiftDemos/srt-godot-test/tree/main/proto

Snippet:
DualStickRawInputCommandBuffer.proto

syntax = "proto2";
package redhatgamedev.srt;

import "box2d.proto";


message DualStickRawInputCommandBuffer
{
    required box2d.PbVec2 pbv2Move = 1;
    required box2d.PbVec2 pbv2Shoot = 2;
}

box2d.proto

syntax = "proto2";
package box2d;

message PbVec2 {
  required float x = 1;
  required float y = 2;
}

Results in compilation error:

/home/thoraxe/Red_Hat/openshift/srt-godot-test/proto/DualStickRawInputCommandBuffer.proto: analysis.
/home/thoraxe/Red_Hat/openshift/srt-godot-test/proto/DualStickRawInputCommandBuffer.proto:9:5: error: Type 'box2d.PbVec2' of the 'pbv2Move' field undefined
/home/thoraxe/Red_Hat/openshift/srt-godot-test/proto/DualStickRawInputCommandBuffer.proto:10:5: error: Type 'box2d.PbVec2' of the 'pbv2Shoot' field undefined
/home/thoraxe/Red_Hat/openshift/srt-godot-test/proto/DualStickRawInputCommandBuffer.proto: analysis error.

It appears that import parsing may not be working correctly. PbVec2 is defined in box2d.proto which is imported by DualStickRawInputCommandBuffer.proto.

Field names can conflict with builtin names

Given the proto:

// Sent from client->server
message Request {
    message Init{}

    oneof kind {
        Init init = 1;
    }
}

Godobuf generates:

class Request:
	func _init():
		var service
		
		_init = PBField.new("init", PB_DATA_TYPE.MESSAGE, PB_RULE.OPTIONAL, 1, true, DEFAULT_VALUES_3[PB_DATA_TYPE.MESSAGE])
		service = PBServiceField.new()
		service.field = _init
		service.func_ref = Callable(self, "new_init")
		data[_init.tag] = service
		
	var data = {}
	
	var _init: PBField

Which fails to compile with Variable "_init" has the same name as a previously declared function.. Maybe generated variable names should use a __ prefix to dodge conflicts?

question about types, services, optional fields

Hi there!

I like your idea of having a GDScript compiler for Godot. However, I need more advanced features of the Protocol Buffers for my use case, thus a few questions.

What is the reason for not supporting types, services, and optional fields?
It is because of the complexity of marshaling or missing features in Godot?
If so, what would be a reasonable approach and the next steps to implement support for them?
Do you consider making a protoc plugin reasonable since that imho would be the regular way to go?

Thanks!

bug: proto3 default map key breaks deserialization

I have a proto3 map field with int32 keys:

message GameState {
    map<int32, PlayerData> player_data = 6;
}

This normally works, except when the map key is 0.

For example, this code is broken because from_bytes returns error REQUIRED_FIELDS:

var s = API.GameState.new()
s.add_player_data(0)
print(s.from_bytes(s.to_bytes()))  # Prints -9, aka "REQUIRED_FIELDS".

However, when I change the map key to something else (e.g. 1), deserialization works fine.

I assume this is due to improper handling of default values as map keys. I haven't tested it, but I assume the same issue is present for other default types (e.g. empty strings).

Add Godot 4 compatible branch

Hi,
I adapted most of the API changes in my fork of godobuf. It is not ready yet to be merged but you can take a look at the changes

https://github.com/kelteseth/godobuf

Godot API Changes:

  • tool -> @tool
  • PoolByteArray -> PackedByteArray
  • String(myInt) -> str(myInt)
  • empty() -> is_empty()
  • remove() -> remove_at(index)
  • dialog.get_rect().size.x -> dialog.size.x
  • .instance() -> .instantiate()
  • funcref(self, "desc_enum"), -> self.desc_enum,
  • template[0].call_func(importance, settings) -> template[0].call(importance, settings)

Buggy:

  • Dialogs open on the wrong screen. Might be Godot 4 bug?
  • When selecting .proto files it asks to override them. Still works though...

Not working:
protobuf_core.gd line 106 and 109 error when using const with []. No idea how to solve this.

const DEFAULT_VALUES_3 = {
        [...]
	#PB_DATA_TYPE.BYTES: [],
	PB_DATA_TYPE.MESSAGE: null,
	#PB_DATA_TYPE.MAP: []
}

This next one looks similar to #26

Parse Error: Identifier "PROTO_VERSION" not declared in the current scope.
  res://addons/protobuf/protobuf_core.gd:533 - Parse Error: Identifier "PROTO_VERSION" not declared in the current scope.`

The identifier "PROTO_VERSION" isn't declared in the current scope.

Trying to use this against Godot 3.4

I am seeing various errors now and again. Here's one that came up with Godot mostly just sitting there at the editor:

ERROR: Failed parse script res://addons/protobuf/protobuf_core.gd
The identifier "PROTO_VERSION" isn't declared in the current scope.
   at: reload_all_workspace_scripts (modules/gdscript/language_server/gdscript_workspace.cpp:217)

Generated classes should extend Reference

Currently, the generated classes extend Object, so when they're no longer used, they need to be deallocated manually or else they will leak (see here). I propose that they should extend Reference so when they're no longer used, they get deallocated automatically.

feature request: unit tests!

The lack of tests makes it somewhat difficult to contribute to this repo because I'm worried that by changing things, I'd be breaking other things :(

feature: Add setter for message fields

I'm curious if we could add setters for fields such as arrays and messages, is there any reason you couldn't? Its quite a pain if you already have an object created to then have to create another and set all the fields.

Add command line generation/auto generation

Protobuf files are used to be shared between multiple projects. For example, I have a CMake/C++ based server that generates the Protobuf c++ files, if I change the content of my messages.proto. For Godot, I have to do it manually and often simply forget to do it. It would be handy to automatically convert the protofile on change (like CMake does) or a way on how to trigger it externally via a command line tool.

Support 'service' in Protobuf

Hello,
Is it possible to add support of service? For example:

syntax = "proto3";

service ServiceName {
    rpc Ping(PingOut) returns (PingIn) {}
}
message PingOut {}
message PingIn {}

feature request: setter for repeated fields

Right now, the only way to "reset" a repeated field seems to be clearing the field (which I believe to be broken due to #21) and re-adding all entries in a loop - for example:

msg.clear_repeated_field()
for item in [1, 2, 3]:
    msg.add_repeated_field(item)

It would be super cool to get a method where we could do this in one statement - i.e. msg.set_repeated_field([1, 2, 3])!

oneof Custom type not supported

message Ping {
    int64 sent_at = 1;
}

message Pong {
    int64 ping_sent_at = 1;
    int64 received_at = 2;
    int64 sent_at = 3;
}

message api_message {
    oneof notice_way {
        Ping ping = 1;
        Pong pong = 2;
    }
    string name = 7;
}
var test2 = apiProto.api_message.new()
test2.set_name("asdad")
test2.set_ping() # miss func

How to fill in values for custom types?

Use typed arrays for fields

Given:

message Foo {
    repeated int32 i = 1;
    repeated string s = 2;
    repeated Bar b = 3;
}

Currently we generate:

get_i() -> Array
get_s() -> Array
get_b() -> Array

With gdscript warnings enabled, this tends to generate lots of warnings in consuming code, and require "unsafe" casts that must be ignored.
It would be nice if we could generate typed return values, like:

get_i() -> Array[int]
get_s() -> Array[string]
get_b() -> Array[Bar]

oneof fields with default values get ignored when packed

Consider a message like this:

message Test {
  oneof foo {
    uint32 field1 = 1;
    uint32 field2 = 2;
  }
}

If I set field1 to 0, which is a default value in proto3, and pack the message, the result is empty. However, I should expect the packed data to include that field1 is set, that it's set to 0, and that field2 is unset.

Support for typed GDScript

GDScript now supports optional types. This feature helps with code readability and maintainability, and it also improves code completion in the editor. It would be great if Godobuf added types to its generated code (in function parameters and return values) to take advantage of this feature.

bug: clearing repeated fields breaks them

I have a repeated proto3 field:

repeated int32 players_in_game = 11;

Adding to the field (msg.add_players_in_game(1)) works fine at first.

However, once you clear the field (msg.clear_players_in_game()), further additions don't work since the field seems to be getting reset to a singular int (I think). Per the language guide, the default value of an cleared repeated field should be an empty array.

Error message:

Invalid call. Nonexistent function 'append' in base 'int'.

For reference, the generated code:

func clear_players_in_game() -> void:
		data[11].state = PB_SERVICE_STATE.UNFILLED
		_players_in_game.value = DEFAULT_VALUES_3[PB_DATA_TYPE.INT32]
func add_players_in_game(value : int) -> void:
		_players_in_game.value.append(value)  # <-- breakage happens here

This is happening on version 0.4.4 on Godot 3.4.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.