From c9aecf2ee3b83ca78be0e4949e3dee56af421b15 Mon Sep 17 00:00:00 2001 From: densestvoid Date: Sun, 17 Jan 2021 08:17:53 -0500 Subject: [PATCH 1/3] Added Target Selector creation with Method Chaining Target Selector struct with commands to add arguments. String function converts the TargetSelector struct to the string format expected in Minecraft commands --- minecraft_entities.go | 144 ++++++++ target_selector_test.go | 709 ++++++++++++++++++++++++++++++++++++++++ target_selectors.go | 431 ++++++++++++++++++++++++ 3 files changed, 1284 insertions(+) create mode 100644 minecraft_entities.go create mode 100644 target_selector_test.go create mode 100644 target_selectors.go diff --git a/minecraft_entities.go b/minecraft_entities.go new file mode 100644 index 0000000..1f6a5fe --- /dev/null +++ b/minecraft_entities.go @@ -0,0 +1,144 @@ +package wrapper + +type minecraftEntityID string + +func (id minecraftEntityID) Namespace() string { + return "minecraft" +} + +func (id minecraftEntityID) Name() string { + return string(id) +} + +func (id minecraftEntityID) String() string { + return id.Namespace() + ":" + id.Name() +} + +// Constants of minecraft entity IDs +const ( + AreaEffectCloudEntity minecraftEntityID = "area_effect_cloud" + ArmorStandEntity minecraftEntityID = "armor_stand" + ArrowEntity minecraftEntityID = "arrow" + BatEntity minecraftEntityID = "bat" + BeeEntity minecraftEntityID = "bee" + BlazeEntity minecraftEntityID = "blaze" + BoatEntity minecraftEntityID = "boat" + CatEntity minecraftEntityID = "cat" + CaveSpiderEntity minecraftEntityID = "cave_spider" + ChestMinecartEntity minecraftEntityID = "chest_minecart" + ChickenEntity minecraftEntityID = "chicken" + CodEntity minecraftEntityID = "cod" + CommandBlockMinecartEntity minecraftEntityID = "command_block_minecart" + CowEntity minecraftEntityID = "cow" + CreeperEntity minecraftEntityID = "creeper" + DolphinEntity minecraftEntityID = "dolphin" + DonkeyEntity minecraftEntityID = "donkey" + DragonFireballEntity minecraftEntityID = "dragon_fireball" + DrownedEntity minecraftEntityID = "drowned" + EggEntity minecraftEntityID = "egg" + ElderGuardianEntity minecraftEntityID = "elder_guardian" + EndCrystalEntity minecraftEntityID = "end_crystal" + EnderDragonEntity minecraftEntityID = "ender_dragon" + EnderPearlEntity minecraftEntityID = "ender_pearl" + EndermanEntity minecraftEntityID = "enderman" + EndermiteEntity minecraftEntityID = "endermite" + EvokerEntity minecraftEntityID = "evoker" + EvokerFangsEntity minecraftEntityID = "evoker_fangs" + ExperienceBottleEntity minecraftEntityID = "experience_bottle" + ExperienceOrbEntity minecraftEntityID = "experience_orb" + EyeOfEnderEntity minecraftEntityID = "eye_of_ender" + FallingBlockEntity minecraftEntityID = "falling_block" + FireballEntity minecraftEntityID = "fireball" + FireworkRocketEntity minecraftEntityID = "firework_rocket" + FishingBobberEntity minecraftEntityID = "fishing_bobber" + FoxEntity minecraftEntityID = "fox" + FurnaceMinecartEntity minecraftEntityID = "furnace_minecart" + GhastEntity minecraftEntityID = "ghast" + GiantEntity minecraftEntityID = "giant" + GuardianEntity minecraftEntityID = "guardian" + HopperMinecartEntity minecraftEntityID = "hopper_minecart" + HorseEntity minecraftEntityID = "horse" + HuskEntity minecraftEntityID = "husk" + IllusionerEntity minecraftEntityID = "illusioner" + IronGolemEntity minecraftEntityID = "iron_golem" + ItemEntity minecraftEntityID = "item" + ItemFrameEntity minecraftEntityID = "item_frame" + LeashKnotEntity minecraftEntityID = "leash_knot" + LightningBoltEntity minecraftEntityID = "lightning_bolt" + LlamaEntity minecraftEntityID = "llama" + LlamaSpitEntity minecraftEntityID = "llama_spit" + MagmaCubeEntity minecraftEntityID = "magma_cube" + MinecartEntity minecraftEntityID = "minecart" + MooshroomEntity minecraftEntityID = "mooshroom" + MuleEntity minecraftEntityID = "mule" + OcelotEntity minecraftEntityID = "ocelot" + PaintingEntity minecraftEntityID = "painting" + PandaEntity minecraftEntityID = "panda" + ParrotEntity minecraftEntityID = "parrot" + PhantomEntity minecraftEntityID = "phantom" + PigEntity minecraftEntityID = "pig" + PillagerEntity minecraftEntityID = "pillager" + PlayerEntity minecraftEntityID = "player" + PolarBearEntity minecraftEntityID = "polar_bear" + PotionEntity minecraftEntityID = "potion" + PufferfishEntity minecraftEntityID = "pufferfish" + RabbitEntity minecraftEntityID = "rabbit" + RavagerEntity minecraftEntityID = "ravager" + SalmonEntity minecraftEntityID = "salmon" + SheepEntity minecraftEntityID = "sheep" + ShulkerEntity minecraftEntityID = "shulker" + ShulkerBulletEntity minecraftEntityID = "shukler_bullet" + SilverfishEntity minecraftEntityID = "silverfish" + SkeletonEntity minecraftEntityID = "skeleton" + SkeletonHorseEntity minecraftEntityID = "skeleton_horse" + SlimeEntity minecraftEntityID = "slime" + SmallFireballEntity minecraftEntityID = "small_fireball" + SnowGolemEntity minecraftEntityID = "snow_golem" + SnowballEntity minecraftEntityID = "snowball" + SpawnerMinecartEntity minecraftEntityID = "spawner_minecart" + SpectralArrowEntity minecraftEntityID = "spectral_arrow" + SpiderEntity minecraftEntityID = "spider" + SquidEntity minecraftEntityID = "squid" + TNTEntity minecraftEntityID = "tnt" + TNTMinecartEntity minecraftEntityID = "tnt_minecart" + TraderLlamaEntity minecraftEntityID = "trader_llama" + TridentEntity minecraftEntityID = "trident" + TropicalFishEntity minecraftEntityID = "tropical_fish" + TurtleEntity minecraftEntityID = "turtle" + VexEntity minecraftEntityID = "vex" + VillagerEntity minecraftEntityID = "villager" + VindicatorEntity minecraftEntityID = "vindicator" + WanderingTraderEntity minecraftEntityID = "wandering_trader" + WitchEntity minecraftEntityID = "witch" + WitherEntity minecraftEntityID = "wither" + WitherSkeletonEntity minecraftEntityID = "wither_skeleton" + WitherSkullEntity minecraftEntityID = "wither_skull" + WolfEntity minecraftEntityID = "wolf" + ZombieEntity minecraftEntityID = "zombie" + ZombieHorseEntity minecraftEntityID = "zombie_horse" + ZombiePigmanEntity minecraftEntityID = "zombie_pigman" + ZombieVillagerEntity minecraftEntityID = "zombie_villager" +) + +type minecraftEntityTypeTag string + +func (tag minecraftEntityTypeTag) Namespace() string { + return "minecaft" +} + +func (tag minecraftEntityTypeTag) Name() string { + return string(tag) +} + +func (tag minecraftEntityTypeTag) String() string { + return "#" + tag.Namespace() + ":" + tag.Name() +} + +// Constants of minecraft entity tags +const ( + Arrows minecraftEntityTypeTag = "arrows" + BeehiveInhabitors minecraftEntityTypeTag = "beehive_inhabitors" + ImpactProjectiles minecraftEntityTypeTag = "impact_projectiles" + Raiders minecraftEntityTypeTag = "raiders" + Skeletons minecraftEntityTypeTag = "skeletons" +) diff --git a/target_selector_test.go b/target_selector_test.go new file mode 100644 index 0000000..a721140 --- /dev/null +++ b/target_selector_test.go @@ -0,0 +1,709 @@ +package wrapper + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +/*//////////////////////////// +//////// Suite Setup //////// +////////////////////////////*/ + +type TargetSelectorSuite struct { + suite.Suite +} + +func (s *TargetSelectorSuite) SetupSuite() {} + +func (s *TargetSelectorSuite) SetupTest() {} + +func (s *TargetSelectorSuite) TearDownTest() {} + +func (s *TargetSelectorSuite) TearDownSuite() {} + +func TestTargetSelectorSuite(t *testing.T) { + suite.Run(t, new(TargetSelectorSuite)) +} + +/*//////////////////////////// +// TargetSelectorType Test // +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestTargetSelectorType() { + type testcase struct { + Name string + TargetSelectorType TargetSelectorType + TargetOutput string + } + + cases := []testcase{ + { + Name: "AllPlayers", + TargetSelectorType: AllPlayers, + TargetOutput: "@a[]", + }, + { + Name: "AllEntities", + TargetSelectorType: AllEntities, + TargetOutput: "@e[]", + }, + { + Name: "NearestPlayer", + TargetSelectorType: NearestPlayer, + TargetOutput: "@p[]", + }, + { + Name: "RandomPlayer", + TargetSelectorType: RandomPlayer, + TargetOutput: "@r[]", + }, + { + Name: "ExecutingEntity", + TargetSelectorType: ExecutingEntity, + TargetOutput: "@s[]", + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + TargetSelector{t: c.TargetSelectorType}.String(), + ) + }) + } +} + +/*//////////////////////////// +//// WithPositional Test //// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithPositional() { + type testcase struct { + Name string + Positional Positional + TargetOutput string + } + + cases := []testcase{ + { + Name: "X", + Positional: Positional{ + Type: X, + Value: 0, + Relative: true, + }, + TargetOutput: "@e[x=~0.00]", + }, + { + Name: "Y", + Positional: Positional{ + Type: Y, + Value: 0, + Relative: false, + }, + TargetOutput: "@e[y=0.00]", + }, + { + Name: "Z", + Positional: Positional{ + Type: Z, + Value: 100.0001, + Relative: true, + }, + TargetOutput: "@e[z=~100.00]", + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithPositional(c.Positional).String(), + ) + }) + } +} + +/*//////////////////////////// +///// WithDistance Test ///// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithDistance() { + type testcase struct { + Name string + Distance Distance + TargetOutput string + } + + cases := []testcase{ + { + Name: "Exact", + Distance: &ExactDistance{10}, + TargetOutput: "@e[distance=10]", + }, + { + Name: "Range", + Distance: &RangeDistance{10, 20}, + TargetOutput: "@e[distance=10..20]", + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithDistance(c.Distance).String(), + ) + }) + } +} + +/*//////////////////////////// +////// WithVolume Test ////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithVolume() { + type testcase struct { + Name string + Volume Volume + TargetOutput string + } + + cases := []testcase{ + { + Name: "DX", + Volume: Volume{ + Type: DX, + Value: 10, + }, + TargetOutput: "@e[dx=10.00]", + }, + { + Name: "DY", + Volume: Volume{ + Type: DY, + Value: 10, + }, + TargetOutput: "@e[dy=10.00]", + }, + { + Name: "DZ", + Volume: Volume{ + Type: DZ, + Value: 10, + }, + TargetOutput: "@e[dz=10.00]", + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithVolume(c.Volume).String(), + ) + }) + } +} + +/*//////////////////////////// +////// WithScore Test ////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithScore() { + type testcase struct { + Name string + Score []Score + TargetOutput string + } + + cases := []testcase{ + { + Name: "Exact", + Score: []Score{ + ExactScore{ + Objective: "test", + Value: 10, + }, + }, + TargetOutput: `@e[score={test=10}]`, + }, + { + Name: "Range", + Score: []Score{ + RangeScore{ + Objective: "test", + Min: 10, + Max: 20, + }, + }, + TargetOutput: "@e[score={test=10..20}]", + }, + { + Name: "Multi", + Score: []Score{ + ExactScore{ + Objective: "a", + Value: 10, + }, + RangeScore{ + Objective: "b", + Min: 10, + Max: 20, + }, + }, + TargetOutput: "@e[score={a=10,b=10..20}]", + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithScore(c.Score...).String(), + ) + }) + } +} + +/*//////////////////////////// +/////// WithTeam Test /////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithTeam() { + type testcase struct { + Name string + Team string + Not bool + TargetOutput string + } + + cases := []testcase{ + { + Name: "TeamA", + Team: "A", + Not: false, + TargetOutput: `@e[team=A]`, + }, + { + Name: "NotTeamA", + Team: "A", + Not: true, + TargetOutput: `@e[team=!A]`, + }, + { + Name: "Teamless", + Team: "", + Not: false, + TargetOutput: `@e[team=]`, + }, + { + Name: "NotTeamless", + Team: "", + Not: true, + TargetOutput: `@e[team=!]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithTeam(c.Team, c.Not).String(), + ) + }) + } +} + +/*//////////////////////////// +/////// WithLimit Test /////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithLimit() { + type testcase struct { + Name string + Limit uint + Sort sortType + TargetOutput string + } + + cases := []testcase{ + { + Name: "WithoutSort", + Limit: 10, + Sort: NoSort, + TargetOutput: `@e[limit=10]`, + }, + { + Name: "WithSort", + Limit: 10, + Sort: Arbitrary, + // TODO: Map order is not guaranteed, needs fixing + TargetOutput: `@e[limit=10,sort=arbitrary]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithLimit(c.Limit, c.Sort).String(), + ) + }) + } +} + +/*//////////////////////////// +//// WithExperience Test //// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithExperience() { + type testcase struct { + Name string + Experience Experience + TargetOutput string + } + + cases := []testcase{ + { + Name: "ExactExperience", + Experience: &ExactExperience{10}, + TargetOutput: `@e[level=10]`, + }, + { + Name: "RangeExperience", + Experience: &RangeExperience{10, 20}, + TargetOutput: `@e[level=10..20]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithExperience(c.Experience).String(), + ) + }) + } +} + +/*//////////////////////////// +///// WithGameMode Test ///// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithGameMode() { + type testcase struct { + Name string + GameMode GameMode + Not bool + TargetOutput string + } + + cases := []testcase{ + { + Name: "Spectator", + GameMode: Spectator, + Not: false, + TargetOutput: `@e[gamemode=spectator]`, + }, + { + Name: "NotSpectator", + GameMode: Spectator, + Not: true, + TargetOutput: `@e[gamemode=!spectator]`, + }, + { + Name: "Adventure", + GameMode: Adventure, + Not: false, + TargetOutput: `@e[gamemode=adventure]`, + }, + { + Name: "NotAdventure", + GameMode: Adventure, + Not: true, + TargetOutput: `@e[gamemode=!adventure]`, + }, + { + Name: "Creative", + GameMode: Creative, + Not: false, + TargetOutput: `@e[gamemode=creative]`, + }, + { + Name: "NotCreative", + GameMode: Creative, + Not: true, + TargetOutput: `@e[gamemode=!creative]`, + }, + { + Name: "Survival", + GameMode: Survival, + Not: false, + TargetOutput: `@e[gamemode=survival]`, + }, + { + Name: "NotSurvival", + GameMode: Survival, + Not: true, + TargetOutput: `@e[gamemode=!survival]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithGameMode(c.GameMode, c.Not).String(), + ) + }) + } +} + +/*//////////////////////////// +/////// WithName Test /////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithName() { + type testcase struct { + Name string + PlayerName string + Not bool + TargetOutput string + } + + cases := []testcase{ + { + Name: "Name", + PlayerName: "Steve", + Not: false, + TargetOutput: `@e[name=Steve]`, + }, + { + Name: "NotName", + PlayerName: "Steve", + Not: true, + TargetOutput: `@e[name=!Steve]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithName(c.PlayerName, c.Not).String(), + ) + }) + } +} + +/*//////////////////////////// +///// WithRotation Test ///// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithRotation() { + type testcase struct { + Name string + Rotation Rotation + TargetOutput string + } + + cases := []testcase{ + { + Name: "Exact", + Rotation: &ExactRotation{XRotation, 10}, + TargetOutput: `@e[x_rotation=10.00]`, + }, + { + Name: "Range", + Rotation: &RangeRotation{YRotation, NewFloat64(10), NewFloat64(20)}, + TargetOutput: `@e[y_rotation=10.00..20.00]`, + }, + { + Name: "RangeMax", + Rotation: &RangeRotation{YRotation, nil, NewFloat64(20)}, + TargetOutput: `@e[y_rotation=..20.00]`, + }, + { + Name: "RangeMin", + Rotation: &RangeRotation{YRotation, NewFloat64(10), nil}, + TargetOutput: `@e[y_rotation=10.00..]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithRotation(c.Rotation).String(), + ) + }) + } +} + +/*//////////////////////////// +/////// WithType Test /////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithType() { + type testcase struct { + Name string + Types []TypeArg + TargetOutput string + } + + cases := []testcase{ + { + Name: "None", + Types: nil, + TargetOutput: `@e[]`, + }, + { + Name: "One", + Types: []TypeArg{ + { + Type: PlayerEntity, + Not: false, + }, + }, + TargetOutput: `@e[type=minecraft:player]`, + }, + { + Name: "NotOne", + Types: []TypeArg{ + { + Type: PlayerEntity, + Not: true, + }, + }, + TargetOutput: `@e[type=!minecraft:player]`, + }, + { + Name: "Multiple", + Types: []TypeArg{ + { + Type: PlayerEntity, + Not: true, + }, + { + Type: EnderDragonEntity, + Not: false, + }, + }, + TargetOutput: `@e[type=!minecraft:player,type=minecraft:ender_dragon]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithType(c.Types...).String(), + ) + }) + } +} + +/*//////////////////////////// +/////// WithType Test /////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestWithDataTag() { + type testcase struct { + Name string + Tags []Tag + TargetOutput string + } + + cases := []testcase{ + { + Name: "None", + Tags: nil, + TargetOutput: `@e[]`, + }, + { + Name: "One", + Tags: []Tag{ + { + Name: "A", + Not: false, + }, + }, + TargetOutput: `@e[tag=A]`, + }, + { + Name: "NotOne", + Tags: []Tag{ + { + Name: "A", + Not: true, + }, + }, + TargetOutput: `@e[tag=!A]`, + }, + { + Name: "Multiple", + Tags: []Tag{ + { + Name: "A", + Not: true, + }, + { + Name: "B", + Not: false, + }, + }, + TargetOutput: `@e[tag=!A,tag=B]`, + }, + } + + for _, c := range cases { + s.Run(c.Name, func() { + s.Assert().Equal( + c.TargetOutput, + NewTargetSelector(AllEntities).WithDataTag(c.Tags...).String(), + ) + }) + } +} + +/*//////////////////////////// +//////// All Example //////// +////////////////////////////*/ + +func (s *TargetSelectorSuite) TestAll() { + expected := TargetSelector{ + t: AllEntities, + args: map[string]string{ + "x": "1.00", + "distance": "2", + "dz": "3.00", + "score": "{score=4..5}", + "team": "!team", + "limit": "6", + "sort": "arbitrary", + "level": "7", + "gamemode": "survival", + "y_rotation": "..8.00", + }, + types: []string{"!minecraft:player"}, + tags: []string{"tag"}, + } + + actual := NewTargetSelector(AllEntities). + WithPositional(Positional{X, 1, false}). + WithDistance(ExactDistance{2}). + WithVolume(Volume{DZ, 3}). + WithScore(RangeScore{"score", 4, 5}). + WithTeam("team", true). + WithLimit(6, Arbitrary). + WithExperience(ExactExperience{7}). + WithGameMode(Survival, false). + WithRotation(RangeRotation{YRotation, nil, NewFloat64(8)}). + WithType(TypeArg{PlayerEntity, true}). + WithDataTag(Tag{"tag", false}) + + s.Assert().EqualValues(expected, actual) +} diff --git a/target_selectors.go b/target_selectors.go new file mode 100644 index 0000000..45fec18 --- /dev/null +++ b/target_selectors.go @@ -0,0 +1,431 @@ +package wrapper + +import ( + "fmt" + "strings" +) + +// TargetSelectorType - an enum type for the 5 different target selectors +type TargetSelectorType string + +const ( + // AllPlayers - targets every player (alive or dead) by default + AllPlayers TargetSelectorType = "a" + // AllEntities - targets all alive entities in loaded chunks (includes players) + AllEntities TargetSelectorType = "e" + // NearestPlayer - targets the nearest player. When run by the console, + // the origin of selection is (0,0,0). If there are multiple nearest + // players, caused by them being precisely the same distance away, the + // payer who most recently joined the server is selected + NearestPlayer TargetSelectorType = "p" + // RandomPlayer - targets a random player + RandomPlayer TargetSelectorType = "r" + // ExecutingEntity - targets the entity (alive or dead) that executed + // the command. It does not target anyhing if the command was run by a + // command block or server console + ExecutingEntity TargetSelectorType = "s" +) + +// TargetSelector - defines and creates a TargetSelector, +// complete with any arguments defined through argument functions +type TargetSelector struct { + t TargetSelectorType + args map[string]string + types []string + tags []string +} + +// NewTargetSelector - creates a new TargetSelector of the specified type +func NewTargetSelector(t TargetSelectorType) TargetSelector { + return TargetSelector{ + t: t, + args: make(map[string]string), + } +} + +// String - returns, in the expected minecraft console format, the string +// version of the TargetSelector +func (s TargetSelector) String() string { + var allArgs []string = make([]string, 0, len(s.args)+len(s.types)+len(s.tags)) + + // args + for key, value := range s.args { + allArgs = append(allArgs, fmt.Sprintf("%s=%s", key, value)) + } + + // types + for _, t := range s.types { + allArgs = append(allArgs, "type="+t) + } + + // tags + for _, tag := range s.tags { + allArgs = append(allArgs, "tag="+tag) + } + + return fmt.Sprintf("@%s[%s]", s.t, strings.Join(allArgs, ",")) +} + +func (s TargetSelector) copyArgs() { + var newMap = make(map[string]string) + for key, val := range s.args { + newMap[key] = val + } + s.args = newMap +} + +func (s TargetSelector) copyStringSlice(slice []string) []string { + return append([]string{}, slice...) +} + +type positionalType string + +// Positional argument types +const ( + X positionalType = "x" + Y positionalType = "y" + Z positionalType = "z" +) + +// Positional - defines a positional argument for TargetSelectors +type Positional struct { + Type positionalType + Value float64 + Relative bool +} + +// WithPositional - adds positional argument(s) to the TargetSelector. +// Returns a new TargetSelector with the positional argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithPositional(args ...Positional) TargetSelector { + s.copyArgs() + for _, arg := range args { + if arg.Relative { + s.args[string(arg.Type)] = fmt.Sprintf("~%.2f", arg.Value) + } else { + s.args[string(arg.Type)] = fmt.Sprintf("%.2f", arg.Value) + } + } + return s +} + +// ExactDistance - specifes the exact distance targets must be from the point of command origin +type ExactDistance struct { + Value uint +} + +func (s ExactDistance) distance() string { + return fmt.Sprintf("%d", s.Value) +} + +// RangeDistance - specifes a range of distance targets can be from the point of command origin +type RangeDistance struct { + Min, Max uint +} + +func (s RangeDistance) distance() string { + return fmt.Sprintf("%d..%d", s.Min, s.Max) +} + +// Distance - accepts either Exacct or Range Distance types +type Distance interface { + distance() string +} + +// WithDistance - adds a diustance argument to the TargetSelector. +// Returns a new TargetSelector with the distance argument added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithDistance(distance Distance) TargetSelector { + s.copyArgs() + s.args["distance"] = distance.distance() + return s +} + +type volumeType string + +// Volume argument types +const ( + DX volumeType = "dx" + DY volumeType = "dy" + DZ volumeType = "dz" +) + +// Volume - defines a volume argument for TargetSelectors +type Volume struct { + Type volumeType + Value float64 +} + +// WithVolume - adds volume argument(s) to the TargetSelector. +// Returns a new TargetSelector with the volume argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithVolume(args ...Volume) TargetSelector { + s.copyArgs() + for _, arg := range args { + s.args[string(arg.Type)] = fmt.Sprintf("%.2f", arg.Value) + } + return s +} + +// ExactScore - specifes the exact score(s) targets must have +type ExactScore struct { + Objective string + Value int +} + +func (s ExactScore) score() string { + return fmt.Sprintf("%s=%d", s.Objective, s.Value) +} + +// RangeScore - specifes a range of score(s) targets can have +type RangeScore struct { + Objective string + Min, Max int +} + +func (s RangeScore) score() string { + return fmt.Sprintf("%s=%d..%d", s.Objective, s.Min, s.Max) +} + +// Score - accepts either an Exact or Range Score types +type Score interface { + score() string +} + +// WithScore - adds score argument(s) to the TargetSelector. +// Returns a new TargetSelector with the score argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithScore(scores ...Score) TargetSelector { + s.copyArgs() + var scoreStrings []string + for _, score := range scores { + scoreStrings = append(scoreStrings, score.score()) + } + scoresJoined := strings.Join(scoreStrings, ",") + s.args["score"] = fmt.Sprintf("{%s}", scoresJoined) + return s +} + +// WithTeam - adds a team argument to the TargetSelector. +// Empty teamName disginates those not on a team. +// Returns a new TargetSelector with the team argument added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithTeam(teamName string, not bool) TargetSelector { + s.copyArgs() + if not { + s.args["team"] = "!" + teamName + } else { + s.args["team"] = teamName + } + return s +} + +type sortType string + +// Sort argument types +const ( + NoSort sortType = "" + Nearest sortType = "nearest" + Furthest sortType = "furthest" + Random sortType = "random" + Arbitrary sortType = "arbitrary" +) + +// WithLimit - adds limit and optionally sort arguments to the TargetSelector. +// Returns a new TargetSelector with the argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithLimit(value uint, sort sortType) TargetSelector { + s.copyArgs() + s.args["limit"] = fmt.Sprintf("%d", value) + if sort != NoSort { + s.args["sort"] = string(sort) + } else { + delete(s.args, "sort") + } + return s +} + +// ExactExperience - specifies the exact level players must be +type ExactExperience struct { + Value uint +} + +func (e ExactExperience) experience() string { + return fmt.Sprintf("%d", e.Value) +} + +// RangeExperience - specifes a range of levels players may be +type RangeExperience struct { + Min, Max uint +} + +func (e RangeExperience) experience() string { + return fmt.Sprintf("%d..%d", e.Min, e.Max) +} + +// Experience - accepts either an Exact or Range Experience types +type Experience interface { + experience() string +} + +// WithExperience - adds limit and optionally sort arguments to the TargetSelector. +// Returns a new TargetSelector with the argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithExperience(exp Experience) TargetSelector { + s.copyArgs() + s.args["level"] = exp.experience() + return s +} + +// WithGameMode - adds a gamemode argument to the TargetSelector. +// Returns a new TargetSelector with the argument added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithGameMode(mode GameMode, not bool) TargetSelector { + s.copyArgs() + if not { + s.args["gamemode"] = "!" + string(mode) + } else { + s.args["gamemode"] = string(mode) + } + return s +} + +// WithName - adds a name argument to the TargetSelector. +// Returns a new TargetSelector with the argument added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithName(name string, not bool) TargetSelector { + s.copyArgs() + if not { + s.args["name"] = "!" + name + } else { + s.args["name"] = name + } + return s +} + +type rotationType string + +// Rotation argument types +const ( + XRotation rotationType = "x_rotation" + YRotation rotationType = "y_rotation" +) + +// ExactRotation - specifies the exact rotation entities must be facing +type ExactRotation struct { + Type rotationType + Value float64 +} + +func (r ExactRotation) rotationType() rotationType { + return r.Type +} + +func (r ExactRotation) rotation() string { + return fmt.Sprintf("%.2f", r.Value) +} + +// NewFloat64 - returns a pointer to the float value passed as an argument +func NewFloat64(f float64) *float64 { return &f } + +// RangeRotation - specifies the range of rotation entities may be facing +type RangeRotation struct { + Type rotationType + Min, Max *float64 +} + +func (r RangeRotation) rotationType() rotationType { + return r.Type +} + +func (r RangeRotation) rotation() string { + var rotStr string + if r.Min != nil { + rotStr += fmt.Sprintf("%.2f", *r.Min) + } + rotStr += ".." + if r.Max != nil { + rotStr += fmt.Sprintf("%.2f", *r.Max) + } + return rotStr +} + +// Rotation - accepts either Exact or Range Rotation types +type Rotation interface { + rotationType() rotationType + rotation() string +} + +// WithRotation - adds rotation argument(s) to the TargetSelector. +// Returns a new TargetSelector with the argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithRotation(rotations ...Rotation) TargetSelector { + s.copyArgs() + for _, rotation := range rotations { + key := rotation.rotationType() + val := rotation.rotation() + s.args[string(key)] = val + } + return s +} + +// TargetSelectorEntityType - an interface for entities, which consist of a namespace and name +// as well as a string format for use in commands. Public so users can satisfy the interface +// for mods and data packs which introduce their own entities. Built-in Minecraft Entity IDS and +// Minecraft Entity tags have been defined +type TargetSelectorEntityType interface { + Namespace() string + Name() string + String() string +} + +// TypeArg - defines a type argument for TargetSelectors +type TypeArg struct { + Type TargetSelectorEntityType + Not bool +} + +// WithType - Appends the type argument(s) to the current +// list of type arguments in the TargetSelector. Only for use with @e. +// Returns a new TargetSelector with the argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithType(args ...TypeArg) TargetSelector { + s.types = s.copyStringSlice(s.types) + for _, arg := range args { + if arg.Not { + s.types = append(s.types, "!"+arg.Type.String()) + } else { + s.types = append(s.types, arg.Type.String()) + } + } + return s +} + +// Tag - defines a tag argument for TargetSelectors. +// Empty Name corresponds to all entities with exactly 0 tags +type Tag struct { + Name string + Not bool +} + +// WithDataTag - Appends the tag argument(s) to the current +// list of tag arguments in the TargetSelector. +// Returns a new TargetSelector with the argument(s) added; +// original is left unchanged. Allows for method chaining +func (s TargetSelector) WithDataTag(tags ...Tag) TargetSelector { + s.tags = s.copyStringSlice(s.tags) + for _, tag := range tags { + if tag.Not { + s.tags = append(s.tags, "!"+tag.Name) + } else { + s.tags = append(s.tags, tag.Name) + } + } + return s +} + +// TODO: WithNBT - requires NBT implementation + +// TODO: WithAdvancements - requires achievement namespace id definitions + +// TODO: WithPredicates - requires predicate implementation From c88eec3087eabeb4f7c94b049b396d872064bf2a Mon Sep 17 00:00:00 2001 From: densestvoid Date: Sun, 17 Jan 2021 08:21:41 -0500 Subject: [PATCH 2/3] Updated go.mod for testing suite usage --- go.mod | 1 + go.sum | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/go.mod b/go.mod index 16c47a6..2861f37 100644 --- a/go.mod +++ b/go.mod @@ -5,4 +5,5 @@ go 1.12 require ( github.com/looplab/fsm v0.1.0 github.com/mitchellh/mapstructure v1.4.0 + github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index eaeeed4..97056d1 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,14 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/looplab/fsm v0.1.0 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20= github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks= github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 735f0a057fe852e058feeffb3a5e6b22dbd78b40 Mon Sep 17 00:00:00 2001 From: densestvoid Date: Sat, 30 Jan 2021 09:50:53 -0500 Subject: [PATCH 3/3] Proposal to rewrite Commands and Events as Interfaces Commands and Events as interfaces allow other packages to be written and use this engine for mods and datapacks, and well as previous minecraft versions --- commands.go | 96 +++++++++++++++++++++ commands/java/1.15.2/gamerule.go | 135 ++++++++++++++++++++++++++++++ commands/java/1.15/gamerule.go | 133 +++++++++++++++++++++++++++++ commands/java/1.16.2/gamerule.go | 137 ++++++++++++++++++++++++++++++ commands/java/1.16/gamerule.go | 137 ++++++++++++++++++++++++++++++ commands/java/1.17/gamerule.go | 139 +++++++++++++++++++++++++++++++ events.go | 41 +++++++++ go.mod | 5 +- go.sum | 7 ++ 9 files changed, 828 insertions(+), 2 deletions(-) create mode 100644 commands.go create mode 100644 commands/java/1.15.2/gamerule.go create mode 100644 commands/java/1.15/gamerule.go create mode 100644 commands/java/1.16.2/gamerule.go create mode 100644 commands/java/1.16/gamerule.go create mode 100644 commands/java/1.17/gamerule.go create mode 100644 events.go diff --git a/commands.go b/commands.go new file mode 100644 index 0000000..7bc5657 --- /dev/null +++ b/commands.go @@ -0,0 +1,96 @@ +package wrapper + +// Reach objective: verify minecraft version (Bedrock vs. Java and 1.XX) to not build / prevent using certain commands + +type Command interface { + Command() string + Events() []Event +} + +// Could be a great spot to use the github.com/densestvoid/postoffice package. +// It was designed to be able to send and receive on channels identified by interface addresses. +// Each event type could be registered as an address +// Event is for any console resposne, error is for command processing only +func (w *Wrapper) ExecuteCommand(cmd Command) (Event, error) { + // TODO: create/get channels for each event type on the wrapper + + // TODO: write the command to the console + + // TODO: wait to receive on one of the event channels, and return that event + + return nil, nil +} + +/* +attribute +advancement +ban x +ban-ip +banlist x +bossbar +clear +clone +data (get) +datapack +debug +defaultgamemode x +deop x +difficulty x +effect +enchant +execute +experience (add,query) +fill +forceload +function +gamemode +gamerule +give x +help +kick x +kill +list x +locate +locatebiome +loot +me +msg +op +pardon +particle +playsound +publish +recipe +reload +save-all x +save-off x +save-on x +say x +schedule +scoreboard +seed +setblock +setidletimeout +setworldspawn +spawnpoint +spectate +spreadplayers +stop x +stopsound +summon +tag +team +teammsg +teleport +tell x +tellraw +time +title +tp +trigger +w +weather +whitelist +worldborder +xp +*/ diff --git a/commands/java/1.15.2/gamerule.go b/commands/java/1.15.2/gamerule.go new file mode 100644 index 0000000..eb185c1 --- /dev/null +++ b/commands/java/1.15.2/gamerule.go @@ -0,0 +1,135 @@ +package minecraft_1_15 + +import ( + "fmt" + + wrapper "github.com/wlwanpan/minecraft-wrapper" +) + +type gameRuleBoolName string + +func (gr gameRuleBoolName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use booleans +const ( + AnnounceAdvancements gameRuleBoolName = "announceAdvancements" + CommandBlockOutput gameRuleBoolName = "commandBlockOutput" + DisableElytraMovementCheck gameRuleBoolName = "disableElytraMovementCheck" + DisableRaids gameRuleBoolName = "disableRaids" + DoDaylightCycle gameRuleBoolName = "doDaylightCycle" + DoEntityDrops gameRuleBoolName = "doEntityDrops" + DoFireTick gameRuleBoolName = "doFireTick" + DoInsomnia gameRuleBoolName = "doInsomnia" + DoImmediateRespawn gameRuleBoolName = "doImmediateRespawn" + DoLimitedCrafting gameRuleBoolName = "doLimitedCrafting" + DoMobLoot gameRuleBoolName = "doMobLoot" + DoMobSpawning gameRuleBoolName = "doMobSpawning" + DoPatrolSpawning gameRuleBoolName = "doPatrolSpawning" // new + DoTileDrops gameRuleBoolName = "doTileDrops" + DoTraderSpawning gameRuleBoolName = "doTraderSpawning" // new + DoWeatherCycle gameRuleBoolName = "doWeatherCycle" + DrowningDamage gameRuleBoolName = "drowningDamage" + FallDamage gameRuleBoolName = "fallDamage" + FireDamage gameRuleBoolName = "fireDamage" + KeepInventory gameRuleBoolName = "keepInventory" + LogAdminCommands gameRuleBoolName = "logAdminCommands" + MobGriefing gameRuleBoolName = "mobGriefing" + NaturalRegeneration gameRuleBoolName = "naturalRegeneration" + ReducedDebugInfo gameRuleBoolName = "reducedDebugInfo" + SendCommandFeedback gameRuleBoolName = "sendCommandFeedback" + ShowDeathMessages gameRuleBoolName = "showDeathMessages" + SpectatorsGenerateChunks gameRuleBoolName = "spectatorsGenerateChunks" +) + +type gameRuleIntName string + +func (gr gameRuleIntName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use integers +const ( + MaxCommandChainLength gameRuleIntName = "maxCommandChainLength" + MaxEntityCramming gameRuleIntName = "maxEntityCramming" + RandomTickSpeed gameRuleIntName = "randomTickSpeed" + SpawnRadius gameRuleIntName = "spawnRadius" +) + +// GameRuleName accepts either boolean or integer name types +type GameRuleName interface { + gameRuleName() string +} + +// GameRule is a command used to set various rules in game +type GameRule struct { + name GameRuleName + bVal bool + iVal int +} + +func NewGameRuleGet(name GameRuleName) GameRule { + return GameRule{name: name} +} + +func NewGameRuleBoolean(name GameRuleName, b bool) GameRule { + return GameRule{name: name, bVal: b} +} + +func NewGameRuleInt(name GameRuleName, i int) GameRule { + return GameRule{name: name, iVal: i} +} + +// Command allows the GameRule struct to be executed as a command in game +func (c GameRule) Command() string { + switch c.name.(type) { + case gameRuleBoolName: + return fmt.Sprintf("gamerule %s %t", c.name.gameRuleName(), c.bVal) + case gameRuleIntName: + return fmt.Sprintf("gamerule %s %d", c.name.gameRuleName(), c.iVal) + default: + return fmt.Sprintf("gamerule %s", c.name.gameRuleName()) + } +} + +func (c GameRule) Events() []wrapper.Event { + return []wrapper.Event{ + &GameRuleSet{}, + &GameRuleGet{}, + &wrapper.IncorrectCommandArgument{}, + &wrapper.InvalidBoolean{}, + &wrapper.InvalidInteger{}, + &wrapper.UnknownOrIncompleteCommand{}, + } +} + +type GameRuleSet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleSet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %d", event.iVal); err != nil { + return false + } + } + return true +} + +type GameRuleGet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleGet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %d", event.iVal); err != nil { + return false + } + } + return true +} diff --git a/commands/java/1.15/gamerule.go b/commands/java/1.15/gamerule.go new file mode 100644 index 0000000..e204eb6 --- /dev/null +++ b/commands/java/1.15/gamerule.go @@ -0,0 +1,133 @@ +package minecraft_1_15_2 + +import ( + "fmt" + + wrapper "github.com/wlwanpan/minecraft-wrapper" +) + +type gameRuleBoolName string + +func (gr gameRuleBoolName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use booleans +const ( + AnnounceAdvancements gameRuleBoolName = "announceAdvancements" + CommandBlockOutput gameRuleBoolName = "commandBlockOutput" + DisableElytraMovementCheck gameRuleBoolName = "disableElytraMovementCheck" + DisableRaids gameRuleBoolName = "disableRaids" + DoDaylightCycle gameRuleBoolName = "doDaylightCycle" + DoEntityDrops gameRuleBoolName = "doEntityDrops" + DoFireTick gameRuleBoolName = "doFireTick" + DoInsomnia gameRuleBoolName = "doInsomnia" // new + DoImmediateRespawn gameRuleBoolName = "doImmediateRespawn" // new + DoLimitedCrafting gameRuleBoolName = "doLimitedCrafting" + DoMobLoot gameRuleBoolName = "doMobLoot" + DoMobSpawning gameRuleBoolName = "doMobSpawning" + DoTileDrops gameRuleBoolName = "doTileDrops" + DoWeatherCycle gameRuleBoolName = "doWeatherCycle" + DrowningDamage gameRuleBoolName = "drowningDamage" // new + FallDamage gameRuleBoolName = "fallDamage" // new + FireDamage gameRuleBoolName = "fireDamage" // new + KeepInventory gameRuleBoolName = "keepInventory" + LogAdminCommands gameRuleBoolName = "logAdminCommands" + MobGriefing gameRuleBoolName = "mobGriefing" + NaturalRegeneration gameRuleBoolName = "naturalRegeneration" + ReducedDebugInfo gameRuleBoolName = "reducedDebugInfo" + SendCommandFeedback gameRuleBoolName = "sendCommandFeedback" + ShowDeathMessages gameRuleBoolName = "showDeathMessages" + SpectatorsGenerateChunks gameRuleBoolName = "spectatorsGenerateChunks" +) + +type gameRuleIntName string + +func (gr gameRuleIntName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use integers +const ( + MaxCommandChainLength gameRuleIntName = "maxCommandChainLength" + MaxEntityCramming gameRuleIntName = "maxEntityCramming" + RandomTickSpeed gameRuleIntName = "randomTickSpeed" + SpawnRadius gameRuleIntName = "spawnRadius" +) + +// GameRuleName accepts either boolean or integer name types +type GameRuleName interface { + gameRuleName() string +} + +// GameRule is a command used to set various rules in game +type GameRule struct { + name GameRuleName + bVal bool + iVal int +} + +func NewGameRuleGet(name GameRuleName) GameRule { + return GameRule{name: name} +} + +func NewGameRuleBoolean(name GameRuleName, b bool) GameRule { + return GameRule{name: name, bVal: b} +} + +func NewGameRuleInt(name GameRuleName, i int) GameRule { + return GameRule{name: name, iVal: i} +} + +// Command allows the GameRule struct to be executed as a command in game +func (c GameRule) Command() string { + switch c.name.(type) { + case gameRuleBoolName: + return fmt.Sprintf("gamerule %s %t", c.name.gameRuleName(), c.bVal) + case gameRuleIntName: + return fmt.Sprintf("gamerule %s %d", c.name.gameRuleName(), c.iVal) + default: + return fmt.Sprintf("gamerule %s", c.name.gameRuleName()) + } +} + +func (c GameRule) Events() []wrapper.Event { + return []wrapper.Event{ + &GameRuleSet{}, + &GameRuleGet{}, + &wrapper.IncorrectCommandArgument{}, + &wrapper.InvalidBoolean{}, + &wrapper.InvalidInteger{}, + &wrapper.UnknownOrIncompleteCommand{}, + } +} + +type GameRuleSet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleSet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %d", event.iVal); err != nil { + return false + } + } + return true +} + +type GameRuleGet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleGet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %d", event.iVal); err != nil { + return false + } + } + return true +} diff --git a/commands/java/1.16.2/gamerule.go b/commands/java/1.16.2/gamerule.go new file mode 100644 index 0000000..4b6d982 --- /dev/null +++ b/commands/java/1.16.2/gamerule.go @@ -0,0 +1,137 @@ +package minecraft_1_16_2 + +import ( + "fmt" + + wrapper "github.com/wlwanpan/minecraft-wrapper" +) + +type gameRuleBoolName string + +func (gr gameRuleBoolName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use booleans +const ( + AnnounceAdvancements gameRuleBoolName = "announceAdvancements" + CommandBlockOutput gameRuleBoolName = "commandBlockOutput" + DisableElytraMovementCheck gameRuleBoolName = "disableElytraMovementCheck" + DisableRaids gameRuleBoolName = "disableRaids" + DoDaylightCycle gameRuleBoolName = "doDaylightCycle" + DoEntityDrops gameRuleBoolName = "doEntityDrops" + DoFireTick gameRuleBoolName = "doFireTick" + DoInsomnia gameRuleBoolName = "doInsomnia" + DoImmediateRespawn gameRuleBoolName = "doImmediateRespawn" + DoLimitedCrafting gameRuleBoolName = "doLimitedCrafting" + DoMobLoot gameRuleBoolName = "doMobLoot" + DoMobSpawning gameRuleBoolName = "doMobSpawning" + DoPatrolSpawning gameRuleBoolName = "doPatrolSpawning" + DoTileDrops gameRuleBoolName = "doTileDrops" + DoTraderSpawning gameRuleBoolName = "doTraderSpawning" + DoWeatherCycle gameRuleBoolName = "doWeatherCycle" + DrowningDamage gameRuleBoolName = "drowningDamage" + FallDamage gameRuleBoolName = "fallDamage" + FireDamage gameRuleBoolName = "fireDamage" + ForgiveDeadPlayers gameRuleBoolName = "forgiveDeadPlayers" + KeepInventory gameRuleBoolName = "keepInventory" + LogAdminCommands gameRuleBoolName = "logAdminCommands" + MobGriefing gameRuleBoolName = "mobGriefing" + NaturalRegeneration gameRuleBoolName = "naturalRegeneration" + ReducedDebugInfo gameRuleBoolName = "reducedDebugInfo" + SendCommandFeedback gameRuleBoolName = "sendCommandFeedback" + ShowDeathMessages gameRuleBoolName = "showDeathMessages" + SpectatorsGenerateChunks gameRuleBoolName = "spectatorsGenerateChunks" + UniversalAnger gameRuleBoolName = "universalAnger" +) + +type gameRuleIntName string + +func (gr gameRuleIntName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use integers +const ( + MaxCommandChainLength gameRuleIntName = "maxCommandChainLength" + MaxEntityCramming gameRuleIntName = "maxEntityCramming" + RandomTickSpeed gameRuleIntName = "randomTickSpeed" + SpawnRadius gameRuleIntName = "spawnRadius" +) + +// GameRuleName accepts either boolean or integer name types +type GameRuleName interface { + gameRuleName() string +} + +// GameRule is a command used to set various rules in game +type GameRule struct { + name GameRuleName + bVal bool + iVal int +} + +func NewGameRuleGet(name GameRuleName) GameRule { + return GameRule{name: name} +} + +func NewGameRuleBoolean(name GameRuleName, b bool) GameRule { + return GameRule{name: name, bVal: b} +} + +func NewGameRuleInt(name GameRuleName, i int) GameRule { + return GameRule{name: name, iVal: i} +} + +// Command allows the GameRule struct to be executed as a command in game +func (c GameRule) Command() string { + switch c.name.(type) { + case gameRuleBoolName: + return fmt.Sprintf("gamerule %s %t", c.name.gameRuleName(), c.bVal) + case gameRuleIntName: + return fmt.Sprintf("gamerule %s %d", c.name.gameRuleName(), c.iVal) + default: + return fmt.Sprintf("gamerule %s", c.name.gameRuleName()) + } +} + +func (c GameRule) Events() []wrapper.Event { + return []wrapper.Event{ + &GameRuleSet{}, + &GameRuleGet{}, + &wrapper.IncorrectCommandArgument{}, + &wrapper.InvalidBoolean{}, + &wrapper.InvalidInteger{}, + &wrapper.UnknownOrIncompleteCommand{}, + } +} + +type GameRuleSet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleSet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %d", event.iVal); err != nil { + return false + } + } + return true +} + +type GameRuleGet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleGet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %d", event.iVal); err != nil { + return false + } + } + return true +} diff --git a/commands/java/1.16/gamerule.go b/commands/java/1.16/gamerule.go new file mode 100644 index 0000000..6e0ac9e --- /dev/null +++ b/commands/java/1.16/gamerule.go @@ -0,0 +1,137 @@ +package minecraft_1_16 + +import ( + "fmt" + + wrapper "github.com/wlwanpan/minecraft-wrapper" +) + +type gameRuleBoolName string + +func (gr gameRuleBoolName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use booleans +const ( + AnnounceAdvancements gameRuleBoolName = "announceAdvancements" + CommandBlockOutput gameRuleBoolName = "commandBlockOutput" + DisableElytraMovementCheck gameRuleBoolName = "disableElytraMovementCheck" + DisableRaids gameRuleBoolName = "disableRaids" + DoDaylightCycle gameRuleBoolName = "doDaylightCycle" + DoEntityDrops gameRuleBoolName = "doEntityDrops" + DoFireTick gameRuleBoolName = "doFireTick" + DoInsomnia gameRuleBoolName = "doInsomnia" + DoImmediateRespawn gameRuleBoolName = "doImmediateRespawn" + DoLimitedCrafting gameRuleBoolName = "doLimitedCrafting" + DoMobLoot gameRuleBoolName = "doMobLoot" + DoMobSpawning gameRuleBoolName = "doMobSpawning" + DoPatrolSpawning gameRuleBoolName = "doPatrolSpawning" + DoTileDrops gameRuleBoolName = "doTileDrops" + DoTraderSpawning gameRuleBoolName = "doTraderSpawning" + DoWeatherCycle gameRuleBoolName = "doWeatherCycle" + DrowningDamage gameRuleBoolName = "drowningDamage" + FallDamage gameRuleBoolName = "fallDamage" + FireDamage gameRuleBoolName = "fireDamage" + ForgiveDeadPlayers gameRuleBoolName = "forgiveDeadPlayers" // new + KeepInventory gameRuleBoolName = "keepInventory" + LogAdminCommands gameRuleBoolName = "logAdminCommands" + MobGriefing gameRuleBoolName = "mobGriefing" + NaturalRegeneration gameRuleBoolName = "naturalRegeneration" + ReducedDebugInfo gameRuleBoolName = "reducedDebugInfo" + SendCommandFeedback gameRuleBoolName = "sendCommandFeedback" + ShowDeathMessages gameRuleBoolName = "showDeathMessages" + SpectatorsGenerateChunks gameRuleBoolName = "spectatorsGenerateChunks" + UniversalAnger gameRuleBoolName = "universalAnger" // new +) + +type gameRuleIntName string + +func (gr gameRuleIntName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use integers +const ( + MaxCommandChainLength gameRuleIntName = "maxCommandChainLength" + MaxEntityCramming gameRuleIntName = "maxEntityCramming" + RandomTickSpeed gameRuleIntName = "randomTickSpeed" + SpawnRadius gameRuleIntName = "spawnRadius" +) + +// GameRuleName accepts either boolean or integer name types +type GameRuleName interface { + gameRuleName() string +} + +// GameRule is a command used to set various rules in game +type GameRule struct { + name GameRuleName + bVal bool + iVal int +} + +func NewGameRuleGet(name GameRuleName) GameRule { + return GameRule{name: name} +} + +func NewGameRuleBoolean(name GameRuleName, b bool) GameRule { + return GameRule{name: name, bVal: b} +} + +func NewGameRuleInt(name GameRuleName, i int) GameRule { + return GameRule{name: name, iVal: i} +} + +// Command allows the GameRule struct to be executed as a command in game +func (c GameRule) Command() string { + switch c.name.(type) { + case gameRuleBoolName: + return fmt.Sprintf("gamerule %s %t", c.name.gameRuleName(), c.bVal) + case gameRuleIntName: + return fmt.Sprintf("gamerule %s %d", c.name.gameRuleName(), c.iVal) + default: + return fmt.Sprintf("gamerule %s", c.name.gameRuleName()) + } +} + +func (c GameRule) Events() []wrapper.Event { + return []wrapper.Event{ + &GameRuleSet{}, + &GameRuleGet{}, + &wrapper.IncorrectCommandArgument{}, + &wrapper.InvalidBoolean{}, + &wrapper.InvalidInteger{}, + &wrapper.UnknownOrIncompleteCommand{}, + } +} + +type GameRuleSet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleSet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %d", event.iVal); err != nil { + return false + } + } + return true +} + +type GameRuleGet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleGet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %d", event.iVal); err != nil { + return false + } + } + return true +} diff --git a/commands/java/1.17/gamerule.go b/commands/java/1.17/gamerule.go new file mode 100644 index 0000000..c51141b --- /dev/null +++ b/commands/java/1.17/gamerule.go @@ -0,0 +1,139 @@ +package minecraft_1_17 + +import ( + "fmt" + + wrapper "github.com/wlwanpan/minecraft-wrapper" +) + +type gameRuleBoolName string + +func (gr gameRuleBoolName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use booleans +const ( + AnnounceAdvancements gameRuleBoolName = "announceAdvancements" + CommandBlockOutput gameRuleBoolName = "commandBlockOutput" + DisableElytraMovementCheck gameRuleBoolName = "disableElytraMovementCheck" + DisableRaids gameRuleBoolName = "disableRaids" + DoDaylightCycle gameRuleBoolName = "doDaylightCycle" + DoEntityDrops gameRuleBoolName = "doEntityDrops" + DoFireTick gameRuleBoolName = "doFireTick" + DoInsomnia gameRuleBoolName = "doInsomnia" + DoImmediateRespawn gameRuleBoolName = "doImmediateRespawn" + DoLimitedCrafting gameRuleBoolName = "doLimitedCrafting" + DoMobLoot gameRuleBoolName = "doMobLoot" + DoMobSpawning gameRuleBoolName = "doMobSpawning" + DoPatrolSpawning gameRuleBoolName = "doPatrolSpawning" + DoTileDrops gameRuleBoolName = "doTileDrops" + DoTraderSpawning gameRuleBoolName = "doTraderSpawning" + DoWeatherCycle gameRuleBoolName = "doWeatherCycle" + DrowningDamage gameRuleBoolName = "drowningDamage" + FallDamage gameRuleBoolName = "fallDamage" + FireDamage gameRuleBoolName = "fireDamage" + ForgiveDeadPlayers gameRuleBoolName = "forgiveDeadPlayers" + FreezeDamage gameRuleBoolName = "freezeDamage" // new + KeepInventory gameRuleBoolName = "keepInventory" + LogAdminCommands gameRuleBoolName = "logAdminCommands" + MobGriefing gameRuleBoolName = "mobGriefing" + NaturalRegeneration gameRuleBoolName = "naturalRegeneration" + ReducedDebugInfo gameRuleBoolName = "reducedDebugInfo" + SendCommandFeedback gameRuleBoolName = "sendCommandFeedback" + ShowDeathMessages gameRuleBoolName = "showDeathMessages" + SpectatorsGenerateChunks gameRuleBoolName = "spectatorsGenerateChunks" + UniversalAnger gameRuleBoolName = "universalAnger" +) + +type gameRuleIntName string + +func (gr gameRuleIntName) gameRuleName() string { + return string(gr) +} + +// GameRule Names that use integers +const ( + MaxCommandChainLength gameRuleIntName = "maxCommandChainLength" + MaxEntityCramming gameRuleIntName = "maxEntityCramming" + PlayersSleepingPercentage gameRuleIntName = "playersSleepingPercentage" // new + RandomTickSpeed gameRuleIntName = "randomTickSpeed" + SpawnRadius gameRuleIntName = "spawnRadius" +) + +// GameRuleName accepts either boolean or integer name types +type GameRuleName interface { + gameRuleName() string +} + +// GameRule is a command used to set various rules in game +type GameRule struct { + name GameRuleName + bVal bool + iVal int +} + +func NewGameRuleGet(name GameRuleName) GameRule { + return GameRule{name: name} +} + +func NewGameRuleBoolean(name GameRuleName, b bool) GameRule { + return GameRule{name: name, bVal: b} +} + +func NewGameRuleInt(name GameRuleName, i int) GameRule { + return GameRule{name: name, iVal: i} +} + +// Command allows the GameRule struct to be executed as a command in game +func (c GameRule) Command() string { + switch c.name.(type) { + case gameRuleBoolName: + return fmt.Sprintf("gamerule %s %t", c.name.gameRuleName(), c.bVal) + case gameRuleIntName: + return fmt.Sprintf("gamerule %s %d", c.name.gameRuleName(), c.iVal) + default: + return fmt.Sprintf("gamerule %s", c.name.gameRuleName()) + } +} + +func (c GameRule) Events() []wrapper.Event { + return []wrapper.Event{ + &GameRuleSet{}, + &GameRuleGet{}, + &wrapper.IncorrectCommandArgument{}, + &wrapper.InvalidBoolean{}, + &wrapper.InvalidInteger{}, + &wrapper.UnknownOrIncompleteCommand{}, + } +} + +type GameRuleSet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleSet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is now set to: %d", event.iVal); err != nil { + return false + } + } + return true +} + +type GameRuleGet struct { + name GameRuleName + bVal *bool + iVal *int +} + +func (event *GameRuleGet) Parse(s string) bool { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %T", event.bVal); err != nil { + if _, err := fmt.Sscanf(s, "Gamerule %s is currently set to: %d", event.iVal); err != nil { + return false + } + } + return true +} diff --git a/events.go b/events.go new file mode 100644 index 0000000..6a463cd --- /dev/null +++ b/events.go @@ -0,0 +1,41 @@ +package wrapper + +import ( + "fmt" +) + +type Event interface { + Parse(string) bool +} + +type IncorrectCommandArgument struct{} + +func (event *IncorrectCommandArgument) Parse(s string) bool { + return s != "Incorrect argument for command" +} + +type InvalidBoolean struct { + Value string +} + +func (event *InvalidBoolean) Parse(s string) bool { + _, err := fmt.Sscanf(s, `Invalid boolean, expected 'true' or 'false' but found '%s'`, &event.Value) + return err == nil +} + +type InvalidInteger struct { + Value string // must be string in case an unrealistically large number is used +} + +func (event *InvalidInteger) Parse(s string) bool { + if _, err := fmt.Sscanf(s, `Invalid integer '%s'`, &event.Value); err != nil { + return false + } + return true +} + +type UnknownOrIncompleteCommand struct{} + +func (event *UnknownOrIncompleteCommand) Parse(s string) bool { + return s != "Unknown or incomplete command, see below for error" +} diff --git a/go.mod b/go.mod index 2861f37..78082e6 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/wlwanpan/minecraft-wrapper go 1.12 require ( - github.com/looplab/fsm v0.1.0 - github.com/mitchellh/mapstructure v1.4.0 + github.com/looplab/fsm v0.2.0 + github.com/mitchellh/mapstructure v1.4.1 + github.com/satori/go.uuid v1.2.0 github.com/stretchr/testify v1.7.0 ) diff --git a/go.sum b/go.sum index 97056d1..5b48bca 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,17 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/looplab/fsm v0.1.0 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20= github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI= +github.com/looplab/fsm v0.2.0 h1:M8hf5EF4AYLcT1FNKVUX8nu7D0xfp291iGeuigSxfrw= +github.com/looplab/fsm v0.2.0/go.mod h1:p+IElwgCnAByqr2DWMuNbPjgMwqcHvTRZZn3dvKEke0= github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks= github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=