blob: e04650be6d7bc8a7c346c56e42269c959d5493c3 [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2015 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// To run, use the `go_sample.sh` script.
18
19package main
20
21import (
22 sample "MyGame/Sample"
23 "fmt"
24 flatbuffers "github.com/google/flatbuffers/go"
25 "strconv"
26)
27
28// Example how to use Flatbuffers to create and read binary buffers.
29func main() {
30 builder := flatbuffers.NewBuilder(0)
31
32 // Create some weapons for our Monster ("Sword" and "Axe").
33 weaponOne := builder.CreateString("Sword")
34 weaponTwo := builder.CreateString("Axe")
35
36 sample.WeaponStart(builder)
37 sample.WeaponAddName(builder, weaponOne)
38 sample.WeaponAddDamage(builder, 3)
39 sword := sample.WeaponEnd(builder)
40
41 sample.WeaponStart(builder)
42 sample.WeaponAddName(builder, weaponTwo)
43 sample.WeaponAddDamage(builder, 5)
44 axe := sample.WeaponEnd(builder)
45
46 // Serialize the FlatBuffer data.
47 name := builder.CreateString("Orc")
48
49 sample.MonsterStartInventoryVector(builder, 10)
50 // Note: Since we prepend the bytes, this loop iterates in reverse.
51 for i := 9; i >= 0; i-- {
52 builder.PrependByte(byte(i))
53 }
54 inv := builder.EndVector(10)
55
56 sample.MonsterStartWeaponsVector(builder, 2)
57 // Note: Since we prepend the weapons, prepend in reverse order.
58 builder.PrependUOffsetT(axe)
59 builder.PrependUOffsetT(sword)
60 weapons := builder.EndVector(2)
61
62 pos := sample.CreateVec3(builder, 1.0, 2.0, 3.0)
63
64 sample.MonsterStart(builder)
65 sample.MonsterAddPos(builder, pos)
66 sample.MonsterAddHp(builder, 300)
67 sample.MonsterAddName(builder, name)
68 sample.MonsterAddInventory(builder, inv)
69 sample.MonsterAddColor(builder, sample.ColorRed)
70 sample.MonsterAddWeapons(builder, weapons)
71 sample.MonsterAddEquippedType(builder, sample.EquipmentWeapon)
72 sample.MonsterAddEquipped(builder, axe)
73 orc := sample.MonsterEnd(builder)
74
75 builder.Finish(orc)
76
77 // We now have a FlatBuffer that we could store on disk or send over a network.
78
79 // ...Saving to file or sending over a network code goes here...
80
81 // Instead, we are going to access this buffer right away (as if we just received it).
82
83 buf := builder.FinishedBytes()
84
85 // Note: We use `0` for the offset here, since we got the data using the
86 // `builder.FinishedBytes()` method. This simulates the data you would store/receive in your
87 // FlatBuffer. If you wanted to read from the `builder.Bytes` directly, you would need to
88 // pass in the offset of `builder.Head()`, as the builder actually constructs the buffer
89 // backwards.
90 monster := sample.GetRootAsMonster(buf, 0)
91
92 // Note: We did not set the `mana` field explicitly, so we get the
93 // default value.
94 assert(monster.Mana() == 150, "`monster.Mana()`", strconv.Itoa(int(monster.Mana())), "150")
95 assert(monster.Hp() == 300, "`monster.Hp()`", strconv.Itoa(int(monster.Hp())), "300")
96 assert(string(monster.Name()) == "Orc", "`string(monster.Name())`", string(monster.Name()),
97 "\"Orc\"")
98 assert(monster.Color() == sample.ColorRed, "`monster.Color()`",
99 strconv.Itoa(int(monster.Color())), strconv.Itoa(int(sample.ColorRed)))
100
101 // Note: Whenever you access a new object, like in `Pos()`, a new temporary accessor object
102 // gets created. If your code is very performance sensitive, you can pass in a pointer to an
103 // existing `Vec3` instead of `nil`. This allows you to reuse it across many calls to reduce
104 // the amount of object allocation/garbage collection.
105 assert(monster.Pos(nil).X() == 1.0, "`monster.Pos(nil).X()`",
106 strconv.FormatFloat(float64(monster.Pos(nil).X()), 'f', 1, 32), "1.0")
107 assert(monster.Pos(nil).Y() == 2.0, "`monster.Pos(nil).Y()`",
108 strconv.FormatFloat(float64(monster.Pos(nil).Y()), 'f', 1, 32), "2.0")
109 assert(monster.Pos(nil).Z() == 3.0, "`monster.Pos(nil).Z()`",
110 strconv.FormatFloat(float64(monster.Pos(nil).Z()), 'f', 1, 32), "3.0")
111
112 // For vectors, like `Inventory`, they have a method suffixed with 'Length' that can be used
113 // to query the length of the vector. You can index the vector by passing an index value
114 // into the accessor.
115 for i := 0; i < monster.InventoryLength(); i++ {
116 assert(monster.Inventory(i) == byte(i), "`monster.Inventory(i)`",
117 strconv.Itoa(int(monster.Inventory(i))), strconv.Itoa(int(byte(i))))
118 }
119
120 expectedWeaponNames := []string{"Sword", "Axe"}
121 expectedWeaponDamages := []int{3, 5}
122 weapon := new(sample.Weapon) // We need a `sample.Weapon` to pass into `monster.Weapons()`
123 // to capture the output of that function.
124 for i := 0; i < monster.WeaponsLength(); i++ {
125 if monster.Weapons(weapon, i) {
126 assert(string(weapon.Name()) == expectedWeaponNames[i], "`weapon.Name()`",
127 string(weapon.Name()), expectedWeaponNames[i])
128 assert(int(weapon.Damage()) == expectedWeaponDamages[i],
129 "`weapon.Damage()`", strconv.Itoa(int(weapon.Damage())),
130 strconv.Itoa(expectedWeaponDamages[i]))
131 }
132 }
133
134 // For FlatBuffer `union`s, you can get the type of the union, as well as the union
135 // data itself.
136 assert(monster.EquippedType() == sample.EquipmentWeapon, "`monster.EquippedType()`",
137 strconv.Itoa(int(monster.EquippedType())), strconv.Itoa(int(sample.EquipmentWeapon)))
138
139 unionTable := new(flatbuffers.Table)
140 if monster.Equipped(unionTable) {
141 // An example of how you can appropriately convert the table depending on the
142 // FlatBuffer `union` type. You could add `else if` and `else` clauses to handle
143 // other FlatBuffer `union` types for this field. (Similarly, this could be
144 // done in a switch statement.)
145 if monster.EquippedType() == sample.EquipmentWeapon {
146 unionWeapon := new(sample.Weapon)
147 unionWeapon.Init(unionTable.Bytes, unionTable.Pos)
148
149 assert(string(unionWeapon.Name()) == "Axe", "`unionWeapon.Name()`",
150 string(unionWeapon.Name()), "Axe")
151 assert(int(unionWeapon.Damage()) == 5, "`unionWeapon.Damage()`",
152 strconv.Itoa(int(unionWeapon.Damage())), strconv.Itoa(5))
153 }
154 }
155
156 fmt.Printf("The FlatBuffer was successfully created and verified!\n")
157}
158
159// A helper function to print out if an assertion failed.
160func assert(assertPassed bool, codeExecuted string, actualValue string, expectedValue string) {
161 if assertPassed == false {
162 panic("Assert failed! " + codeExecuted + " (" + actualValue +
163 ") was not equal to " + expectedValue + ".")
164 }
165}