Austin Schuh | e89fa2d | 2019-08-14 20:24:23 -0700 | [diff] [blame^] | 1 | /* |
| 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 | |
| 19 | package main |
| 20 | |
| 21 | import ( |
| 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. |
| 29 | func 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. |
| 160 | func 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 | } |