├── .gitignore ├── .idea ├── .name ├── IdeaProject.iml ├── codeStyleSettings.xml ├── compiler.xml ├── encodings.xml ├── highlighting.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── libraries │ ├── SBT__com_joshcough_scala_minecraft_plugin_api_2_11_0_3_3.xml │ ├── SBT__javax_servlet_servlet_api_2_5_provided.xml │ ├── SBT__jline_jline_2_11.xml │ ├── SBT__org_bukkit_bukkit_1_7_2_R0_2.xml │ ├── SBT__org_clojure_clojure_1_4_0.xml │ ├── SBT__org_scala_lang_modules_scala_parser_combinators_2_11_1_0_1_test.xml │ ├── SBT__org_scala_lang_scala_library_2_11_0.xml │ ├── SBT__org_scala_sbt_test_interface_1_0_test.xml │ ├── SBT__org_scalacheck_scalacheck_2_11_1_11_3_test.xml │ ├── SBT__scala_2_10_3.xml │ └── servlet_api_2_5.xml ├── misc.xml ├── modules.xml ├── projectCodeStyle.xml ├── scala_compiler.xml ├── scala_settings.xml ├── scopes │ └── scope_settings.xml └── vcs.xml ├── .idea_modules ├── Arena.iml ├── BanArrows.iml ├── BlockChanger.iml ├── BlockChangerGold.iml ├── Farmer.iml ├── GetOffMyLawn.iml ├── God.iml ├── LightningArrows.iml ├── MultiPlayerCommands.iml ├── NoRain.iml ├── PluginCommander.iml ├── Shock.iml ├── TeleportBows.iml ├── Thor.iml ├── TreeDelogger.iml ├── WorldEdit.iml ├── YellowBrickRoad.iml ├── ZombieApocalypse.iml ├── all-build.iml ├── all.iml ├── commonPlugins.iml ├── core.iml ├── microexample-build.iml ├── microexample.iml ├── mineLang.iml └── scalaLibPlugin.iml ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── core └── src │ ├── main │ ├── resources │ │ └── plugin.yml │ └── scala │ │ └── com │ │ └── joshcough │ │ └── minecraft │ │ ├── BukkitEnrichment.scala │ │ ├── CommandsPlugin.scala │ │ ├── CommonCommands.scala │ │ ├── Cube.scala │ │ ├── CubeModifier.scala │ │ ├── CubeState.scala │ │ ├── DBPlugin.scala │ │ ├── Listeners.scala │ │ ├── NPCPlugin.scala │ │ ├── ParserCombinators.scala │ │ ├── PlayerState.scala │ │ ├── ScalaEnrichment.scala │ │ ├── ScalaPlugin.scala │ │ ├── TestServer.scala │ │ ├── UndoState.scala │ │ └── YMLGenerator.scala │ └── test │ └── scala │ └── com │ └── joshcough │ └── minecraft │ ├── CubeTests.scala │ ├── MinecraftParsersTests.scala │ ├── ParserCombinatorDemo.scala │ ├── ParserCombinatorTests.scala │ ├── ScalaEnrichmentTests.scala │ └── TestHelpers.scala ├── docs ├── Making Java APIs usable with Scala.pdf ├── bukkit.md └── changelog.md ├── examples ├── Arena │ └── Arena.scala ├── BanArrows │ └── BanArrows.scala ├── BlockChanger │ └── BlockChanger.scala ├── BlockChangerGold │ └── BlockChangerGold.scala ├── Farmer │ └── Farmer.scala ├── GetOffMyLawn │ └── GetOffMyLawn.scala ├── God │ └── God.scala ├── LightningArrows │ └── LightningArrows.scala ├── MultiPlayerCommands │ └── MultiPlayerCommands.scala ├── NoRain │ └── NoRain.scala ├── PluginCommander │ └── PluginCommander.scala ├── Shock │ └── Shock.scala ├── Thor │ └── Thor.scala ├── TreeDelogger │ └── TreeDelogger.scala ├── WorldEdit │ └── WorldEdit.scala ├── YellowBrickRoad │ └── YellowBrickRoad.scala ├── ZombieApocalypse │ └── ZombieApocalypse.scala └── src │ ├── main │ └── scala │ │ └── com │ │ └── joshcough │ │ └── minecraft │ │ └── examples │ │ ├── CopyServer.scala │ │ ├── NPCTest.scala │ │ ├── SimplePlugins.scala │ │ ├── WarpPlugin.scala │ │ └── WorldEditDemo.scala │ └── test │ └── scala │ └── com │ └── joshcough │ └── minecraft │ └── examples │ └── CopyDataTests.scala ├── microexample ├── BlockChanger.scala ├── Makefile ├── README.md ├── build.sbt ├── project │ ├── build.properties │ ├── plugins.sbt │ └── project │ │ └── plugins.sbt └── sbt ├── other ├── clojure │ └── src │ │ ├── main │ │ └── clojure │ │ │ └── com │ │ │ └── joshcough │ │ │ └── minecraft │ │ │ └── testplugin.clj │ │ └── test │ │ └── clojure │ │ └── com │ │ └── joshcough │ │ └── minecraft │ │ └── test │ │ └── testplugin_test.clj └── minelang │ └── src │ ├── main │ ├── resources │ │ ├── clojureinscala │ │ │ ├── bool.mc │ │ │ ├── list.mc │ │ │ └── math.mc │ │ └── minelang │ │ │ ├── expand.mc │ │ │ ├── factorial.mc │ │ │ ├── house.mc │ │ │ └── house2.mc │ └── scala │ │ └── com │ │ └── joshcough │ │ └── minecraft │ │ ├── ClojureInScala.scala │ │ ├── MineLang.scala │ │ └── MineLangPlugin.scala │ └── test │ └── scala │ └── com │ └── joshcough │ └── minecraft │ ├── ClojureInScalaTests.scala │ ├── MineLangTests.scala │ └── TestHelpers.scala ├── project ├── build.properties ├── build.scala ├── plugins.sbt └── project │ └── plugins.sbt ├── sbt └── scala-lib-plugin ├── build.sbt └── src └── main ├── resources └── plugin.yml └── scala └── com └── joshcough └── minecraft └── ScalaLibPlugin.scala /.gitignore: -------------------------------------------------------------------------------- 1 | examples/src/main/resources/ 2 | target/ 3 | out/ 4 | .idea/workspace.xml 5 | .idea/copyright/ 6 | .lib/ 7 | plugins/ 8 | bukkit/ 9 | .DS_Store 10 | *.jar 11 | *.class 12 | .lein-deps-sum 13 | .lein-failures 14 | .lein-plugins 15 | .ermine_history 16 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | MinecraftPlugins -------------------------------------------------------------------------------- /.idea/IdeaProject.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/highlighting.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__com_joshcough_scala_minecraft_plugin_api_2_11_0_3_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__javax_servlet_servlet_api_2_5_provided.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__jline_jline_2_11.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_bukkit_bukkit_1_7_2_R0_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_clojure_clojure_1_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_scala_lang_modules_scala_parser_combinators_2_11_1_0_1_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_scala_lang_scala_library_2_11_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_scala_sbt_test_interface_1_0_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__org_scalacheck_scalacheck_2_11_1_11_3_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/SBT__scala_2_10_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/servlet_api_2_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | http://www.w3.org/1999/xhtml 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/projectCodeStyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 10 | -------------------------------------------------------------------------------- /.idea/scala_compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/scala_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/scopes/scope_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea_modules/Arena.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/BanArrows.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/BlockChanger.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/BlockChangerGold.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/Farmer.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/GetOffMyLawn.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/God.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/LightningArrows.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/MultiPlayerCommands.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/NoRain.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/PluginCommander.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/Shock.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/TeleportBows.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/Thor.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/TreeDelogger.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/WorldEdit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/YellowBrickRoad.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/ZombieApocalypse.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea_modules/all-build.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /.idea_modules/all.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea_modules/commonPlugins.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.idea_modules/core.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea_modules/microexample-build.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /.idea_modules/microexample.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.idea_modules/mineLang.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.idea_modules/scalaLibPlugin.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | jdk: 3 | - oraclejdk8 4 | scala: 5 | - 2.11.6 6 | script: 7 | - sbt 'set parallelExecution in Test := false' test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2013 Josh Cough 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | core-release: 2 | echo "copying documentation to pages dir" 3 | rm -rf ../pages/MinecraftPlugins/scaladoc/scala-plugin-api_2.10.3-0.3.3 4 | cp -r scala/core/target/scala-2.10/api ../pages/MinecraftPlugins/scaladoc/scala-plugin-api_2.10.3-0.3.3 5 | echo "don't forget:" 6 | echo " * commit the scaladocs after adding a link in scaladoc/index.html" 7 | echo " * upload the release" 8 | echo " * apply git tag for 0.3.3" 9 | echo " * bring MinecraftPluginsScalaExample up to date" 10 | echo " * consider automating some of these!" 11 | 12 | go: 13 | cd $(BUKKIT); java -Djava.ext.dirs=/Library/Java/Home/lib/ext:lib -Dorg.nlogo.noGenerator=true -Xmx2048M -Xms512M -jar bukkit.jar nogui 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/joshcough/MinecraftPlugins.png?branch=master)](https://travis-ci.org/joshcough/MinecraftPlugins) 2 | [![Join the chat at https://gitter.im/joshcough/MinecraftPlugins](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/joshcough/MinecraftPlugins?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | 4 | # Overview 5 | 6 | This project contains an API for building Minecraft Plugins in Scala. 7 | It is currently made up of several sub-projects, but the most important ones to know about are: 8 | 9 | * core - The core API 10 | * microexample - A first example of how to create a plugin in Scala. 11 | * examples - Several more example plugins using the core API 12 | * scala-lib-plugin - Provides a Bukkit plugin that provides the Scala API to other plugins at runtime. 13 | 14 | # Why? 15 | 16 | There are several reasons for building this API, but the three that pop to mind immediately are: 17 | 18 | * To show how easy it is to put better/cleaner APIs on top of existing Java APIs. 19 | * To make command and argument parsing vastly easier using parser combinators. 20 | * To hopefully spark some interest in Scala among the Minecraft community. 21 | 22 | Note: The core API is built on Bukkit version 1.7.2-R0.2 (http://bukkit.org/), which works with Minecraft 1.7.2. 23 | 24 | ## Examples 25 | 26 | ### Listener example: LightningArrows 27 | 28 | The following example demonstrates how to use listeners in Scala. 29 | This plugin strikes an entity (any player or mob) with lightning 30 | if that entity is hit with an arrow: 31 | 32 | ```scala 33 | import com.joshcough.minecraft.Listeners._ 34 | import org.bukkit.entity.EntityType.ARROW 35 | 36 | class LightningArrows extends ListeningFor(OnEntityDamageByEntity { e => 37 | if (e.getDamager isAn ARROW) e.world.strikeLightning(e.loc) 38 | }) 39 | ``` 40 | 41 | ### Commands example: Shock 42 | 43 | The following example demonstrates how to write commands in Scala. 44 | It has one command called "shock" which takes a player as input, 45 | and shocks that player with lightning. 46 | 47 | ```scala 48 | import com.joshcough.minecraft.CommandPlugin 49 | 50 | class Shock extends CommandPlugin { 51 | val command = Command("shock", "shock a player", player){ case (you, them) => 52 | them.shock 53 | } 54 | } 55 | ``` 56 | 57 | ## Other Stuff 58 | 59 | 60 | #### Other subprojects 61 | 62 | There are a few other sub projects and I'll mention them just very briefly here. 63 | 64 | * netlogo - A plugin that enables running NetLogo models in Minecraft servers. 65 | More info here: [NetLogo Plugin](https://github.com/joshcough/MinecraftPlugins/wiki/NetLogo) 66 | * minelang - An ongoing attempt at writing my own language 67 | (very much like Clojure, but not as good) that allows users to write their own 68 | minecraft plugins without having to know anything about IDE's and Jar files and 69 | stuff like that. I don't have any other information to give on this just yet, though. 70 | 71 | #### Links 72 | 73 | * [Project site on Bukkit](http://dev.bukkit.org/server-mods/scala-plugin-api/) 74 | -------------------------------------------------------------------------------- /core/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ScalaPluginAPI 2 | main: com.joshcough.minecraft.ScalaPluginAPI 3 | author: Josh Cough 4 | version: 0.3.3 5 | database: false 6 | depend: [ScalaLibPlugin] 7 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/CommonCommands.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.GameMode._ 4 | import scala.collection.JavaConversions._ 5 | 6 | /** 7 | * Some common commands that I found myself writing over and over again 8 | * for various plugins. I wanted to put them all into one location, 9 | * so that they could be reused elsewhere. 10 | */ 11 | trait CommonCommands { 12 | 13 | import CommandsPlugin._ 14 | 15 | // some simple useful commands 16 | val goto = Command("goto", "Teleport to a player.", player or location){ 17 | case (you, Left(them)) => you.teleportTo(them) 18 | case (you, Right(loc)) => you.teleport(loc of you.world) 19 | } 20 | val timeCommand = Command("set-time", "Sets the time.", time){ 21 | case (p, n) => p.world.setTime(n) 22 | } 23 | val day = Command("day", "Sets the time to 1." )(_.world.setTime(1)) 24 | val night = Command("night", "Sets the time to 15000.")(_.world.setTime(15000)) 25 | val gm = Command("gm", "Set your game mode", gamemode){ case (p, gm) => p.setGameMode(gm) } 26 | val gms = Command("gms", "Set your game mode to survival.")(_.setGameMode(SURVIVAL)) 27 | val gmc = Command("gmc", "Set your game mode to creative.")(_.setGameMode(CREATIVE)) 28 | val kill = Command("kill", "Kill entities.", ("player" ~ player) or entity){ 29 | case (killer, Left(_ ~ deadMan)) => killer.kill(deadMan) 30 | case (killer, Right(e)) => killer.world.entities.filter { _ isAn e }.foreach(_.remove) 31 | } 32 | 33 | val allCommonCommands = List(goto, timeCommand, day, night, gm, gms, gmc, kill) 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/CubeModifier.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.block.Block 4 | import org.bukkit.Material 5 | import com.joshcough.minecraft.BukkitEnrichment._ 6 | import Cube._ 7 | 8 | // GIANT TODO 9 | // GIANT TODO: force some of the streams in mirroring! 10 | // GIANT TODO 11 | object CubeModifier { 12 | 13 | object PotentialChange { 14 | def apply(c: Change) = new PotentialChange(c.b, c.oldM) 15 | def apply(b: Block, m: Material) = new PotentialChange(b, m.andData) 16 | } 17 | 18 | /** 19 | * Represents a change that might happen in the world at some later time. 20 | * @param b 21 | * @param newM 22 | */ 23 | case class PotentialChange(b: Block, newM: MaterialAndData){ 24 | val oldM = b.materialAndData 25 | def run: Boolean = newM update b 26 | } 27 | 28 | type PotentialChanges = Stream[PotentialChange] 29 | 30 | /** 31 | * Represents a change that actually took place in the world. 32 | * @param b The block that was changed. 33 | * @param oldM The blocks previous material before it was changed 34 | */ 35 | case class Change(b: Block, oldM: MaterialAndData){ 36 | override def toString = s"Change(b:${b.loc.xyz} m:${oldM.m.name})" 37 | } 38 | 39 | /** 40 | * Represents a number of changes that actually took place in the world. 41 | * @param cs 42 | */ 43 | case class Changes(cs:Array[Change]){ 44 | override def toString = cs.toList.mkString(",") 45 | def size = cs.length 46 | def ++(cs: Changes) = Changes(this.cs ++ cs.cs) 47 | } 48 | 49 | /** 50 | * Actually execute some PotentialChanges, 51 | * handing back a Seq of all the changes that really took place. 52 | * (A potential change might not happen, if for example, you try to change AIR to AIR.) 53 | * @param newData 54 | * @return 55 | */ 56 | def runChanges(newData: Seq[PotentialChange]): Changes = 57 | Changes(newData.filter(_.run).map(p => Change(p.b, p.oldM)).toArray) 58 | 59 | /** 60 | * TODO: document me! 61 | */ 62 | def getTransformationChanges(cube: Cube[Block], 63 | force: Boolean = false): Stream[PotentialChange] = { 64 | val s = cube.toZippedStream.map{ case (c,b) => 65 | PotentialChange(cube.world(c.x, c.y, c.z), b.materialAndData) 66 | } 67 | if(force) s.force else s 68 | } 69 | 70 | def translateAll(cube: Cube[Block], force: Boolean = false): Changes = 71 | runChanges(getTransformationChanges(cube, force)) 72 | 73 | /** 74 | * Set all the blocks in this cube to the given Material 75 | */ 76 | def setAll(c: Cube[Block], newM: Material): Changes = setAll(c.blocks, newM.andData) 77 | 78 | /** 79 | * Set all the blocks in this stream to the given Material 80 | */ 81 | def setAll(bms: Stream[Block], newM: MaterialAndData) = runChanges( 82 | bms.zip(Stream.continually(newM)).map{ case (b,n) => PotentialChange(b,n) } 83 | ) 84 | 85 | /** 86 | * Change all the blocks of the old material type to the new material type. 87 | */ 88 | def changeAll(c: Cube[Block], oldM: Material, newM: MaterialAndData): Changes = 89 | setAll(c.blocks.filter(_ is oldM), newM) 90 | 91 | /** 92 | * Set all the blocks in this cube to air 93 | * TODO: this really could be removed... 94 | */ 95 | def eraseAll(c: Cube[Block]): Changes = setAll(c.blocks, MaterialAndData.AIR) 96 | } 97 | 98 | // case class PotentialSwap(b1: Block, b2: Block){ 99 | // def run: Seq[Change] = { 100 | // val oldB1M = b1.materialAndData 101 | // val oldB2M = b2.materialAndData 102 | // List( 103 | // oldB1M.update(b2).toOption(Change(b2, oldB2M)), 104 | // oldB2M.update(b1).toOption(Change(b1, oldB1M)) 105 | // ).flatten 106 | // } 107 | // } 108 | // def runSwaps(swaps: Seq[PotentialSwap]): Changes = Changes(swaps.flatMap(_.run).toArray) 109 | 110 | 111 | // 112 | // def paste(newL1: Location): Changes = Changer.runChanges(run(c.paste(newL1.coor))) 113 | // def pasteMirrorY(newL1: Location): Changes = Changer.runChanges(run(c.paste(newL1.coor).mirrorY)) 114 | 115 | // /** 116 | // * We have to force these (.force), because if they are run lazily, 117 | // * then a will be replaced with b, and later when b trieds to get replaced with a, 118 | // * a's material type is already what b is, so b just gets set to itself. 119 | // * Forcing guarantees that we get the right data values in the list. 120 | // * @return 121 | // */ 122 | // def mirrorXChanges: Changes = Changer.runChanges(run(c.mirrorX).force) 123 | // def mirrorYChanges: Changes = Changer.runChanges(run(c.mirrorY).force) 124 | // def mirrorZChanges: Changes = Changer.runChanges(run(c.mirrorZ).force) 125 | // 126 | // /** 127 | // * @param newL1 128 | // */ 129 | // def move(newL1: Location): Changes = paste(newL1) ++ setAll(Material.AIR) 130 | 131 | //import org.squeryl.{KeyedEntity, Schema} 132 | //import org.squeryl.dsl.{OneToMany, ManyToOne} 133 | //import org.squeryl.PrimitiveTypeMode._ 134 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/CubeState.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import BukkitEnrichment._ 4 | import org.bukkit.Location 5 | import org.bukkit.block.Block 6 | import org.bukkit.entity.Player 7 | 8 | trait CubeState { 9 | 10 | val corners = new PlayerState[List[Location]] { 11 | override val default = Some(Nil) 12 | } 13 | 14 | def cube(p:Player): Cube[Block] = 15 | cubeMaybe(p).getOrElse(p bomb "Both corners must be set!") 16 | 17 | def cubeMaybe(p:Player): Option[Cube[Block]] = corners(p) match { 18 | case List(c1, c2) => Some(c1 cubeTo c2) 19 | case _ => None 20 | } 21 | 22 | def setFirstPosition(p:Player,loc: Location): Unit = { 23 | corners += (p -> List(loc)) 24 | p ! s"first corner set to: ${loc.xyz}" 25 | } 26 | 27 | def setSecondPosition(p:Player,loc2: Location): Unit = corners(p) match { 28 | case loc1 :: _ => 29 | corners += (p -> List(loc1, loc2)) 30 | p ! s"second corner set to: ${loc2.xyz}" 31 | case Nil => 32 | p ! "set corner one first! (with a left click)" 33 | } 34 | 35 | def cubes: collection.Map[Player, Cube[Block]] = 36 | corners.state.filter(kv => kv._2.size == 2).mapValues { 37 | case List(l1, l2) => l1 cubeTo l2 38 | } 39 | } -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/DBPlugin.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft 2 | // 3 | //import org.squeryl.adapters.DerbyAdapter 4 | //import org.squeryl.{Table, PrimitiveTypeMode, Session, Schema} 5 | //import PrimitiveTypeMode._ 6 | //import org.bukkit.entity.Player 7 | //import org.squeryl.internals.DatabaseAdapter 8 | // 9 | //object DB { 10 | // Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance() 11 | // def session(connectionURL: String, adapter: DatabaseAdapter = new DerbyAdapter) = 12 | // Session.create( 13 | // java.sql.DriverManager.getConnection(connectionURL), adapter 14 | // ) 15 | //} 16 | // 17 | //trait DBPlugin extends ScalaPlugin { 18 | // 19 | // val db: Schema 20 | // private def dbFile = new java.io.File(this.getDataFolder, this.name + ".db") 21 | // private def connectionURL = s"jdbc:derby:${dbFile.getAbsolutePath};create=true" 22 | // def newSession: Session = DB.session(connectionURL) 23 | // 24 | // def initializeDB: Unit = { 25 | // this.getDataFolder.mkdirs 26 | // PrimitiveTypeMode.transaction(newSession)(try db.create catch { case e: Exception => }) 27 | // logInfo("database initialized") 28 | // } 29 | // 30 | // def transaction[A](a: => A): A = PrimitiveTypeMode.transaction(newSession)(a) 31 | // def runQuery[A](a: => A): A = transaction(a) 32 | // 33 | // override def onEnable(){ 34 | // initializeDB 35 | // super.onEnable() 36 | // } 37 | //} 38 | // 39 | //trait DBPluginWithCommands extends DBPlugin with CommandsPlugin { 40 | // def DBCommand(name: String, desc: String)(body: Player => Unit): Command = 41 | // DBCommand(name, desc, eof){ case (p, _) => body(p) } 42 | // 43 | // def DBCommand[T](name: String, desc: String, args: Parser[T]) 44 | // (body: ((Player, T)) => Unit): Command = 45 | // Command(name, desc, args){ case (p, t) => transaction(body(p, t)) } 46 | //} -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/Listeners.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.block.Block 4 | import org.bukkit.entity.{Arrow, Entity, Player} 5 | import org.bukkit.event.{EventHandler => EH, Listener} 6 | import org.bukkit.event.block.{BlockBreakEvent, BlockDamageEvent} 7 | import org.bukkit.event.block.Action._ 8 | import org.bukkit.event.entity.{ProjectileHitEvent, EntityDamageEvent, PlayerDeathEvent, EntityDamageByEntityEvent} 9 | import org.bukkit.event.weather.WeatherChangeEvent 10 | import org.bukkit.event.player.{PlayerQuitEvent, PlayerInteractEvent, PlayerMoveEvent, PlayerChatEvent, 11 | PlayerJoinEvent, PlayerKickEvent, PlayerLoginEvent} 12 | 13 | /** 14 | * A trait that supports exactly one listener. 15 | * This is really just a tiny convenience wrapper over ListenersPlugin, 16 | * so that you can say: 17 | * 18 | * val listener = createMyListener 19 | * 20 | * instead of: 21 | * 22 | * val listeners = List(createMyListener) 23 | */ 24 | trait ListenerPlugin extends ListenersPlugin { 25 | def listener: Listener 26 | def listeners = List(listener) 27 | override def onEnable(){ super.onEnable(); registerListener(listener) } 28 | } 29 | 30 | /** 31 | * A trait that can have many Listeners. 32 | * 33 | * All clients need to do is specify the listeners val, like so: 34 | * 35 | * val listeners = List( 36 | * createListener1, 37 | * createListener2, 38 | * ... 39 | * ) 40 | * 41 | * Convenience functions for creating Listeners are provided in the Listeners trait. 42 | */ 43 | trait ListenersPlugin extends ScalaPlugin with Listeners { 44 | def listeners: List[Listener] 45 | override def onEnable{ super.onEnable(); listeners.foreach(registerListener) } 46 | } 47 | 48 | object Listeners extends Listeners 49 | object ListenersObject extends Listeners 50 | 51 | /** 52 | * This trait supports many convenience wrappers for creating Listeners with 53 | * higher order functions. Creating Listeners in Bukkit is fairly awkward. 54 | * You have to create a Listener instance with an annotated method, 55 | * that method can have any name, and it must take some Event as an argument, like so: 56 | * 57 | * new Listener { 58 | * @EventHandler def on(e:PlayerMoveEvent): Unit = doSomething(e) 59 | * } 60 | * 61 | * This is all abstracted away from the user here. A user now just says: 62 | * 63 | * OnPlayerMove(doSomething) 64 | * 65 | * (where doSomething is just a function that takes a PlayerMoveEvent, same as above) 66 | * 67 | * There are piles of examples of this in the examples code. 68 | **/ 69 | trait Listeners extends BukkitEnrichment { 70 | abstract case class ListeningFor(listener:Listener) extends ListenerPlugin 71 | 72 | def OnPlayerMove(f: PlayerMoveEvent => Unit) = new Listener { 73 | @EH def on(e:PlayerMoveEvent): Unit = f(e) 74 | } 75 | def OnEntityDamageByEntity(f: EntityDamageByEntityEvent => Unit) = new Listener { 76 | @EH def on(e:EntityDamageByEntityEvent): Unit = f(e) 77 | } 78 | def OnPlayerDamageByEntity(f: (Player, EntityDamageByEntityEvent) => Unit) = new Listener { 79 | @EH def on(e:EntityDamageByEntityEvent): Unit = e.getEntity.whenPlayer(f(_, e)) 80 | } 81 | def OnEntityDamageByPlayer(f: (Entity, Player, EntityDamageByEntityEvent) => Unit) = new Listener { 82 | @EH def on(e:EntityDamageByEntityEvent): Unit = e.getDamager match { 83 | case p: Player => f(e.getEntity,p, e) 84 | case _ => 85 | } 86 | } 87 | def OnPlayerDamage(f: (Player, EntityDamageEvent) => Unit) = new Listener { 88 | @EH def on(e:EntityDamageEvent): Unit = e.getEntity.whenPlayer(f(_, e)) 89 | } 90 | def OnPlayerDeath(f: (Player, PlayerDeathEvent) => Unit) = new Listener { 91 | @EH def on(e:PlayerDeathEvent): Unit = f(e.getEntity, e) 92 | } 93 | def OnPlayerChat(f: (Player, PlayerChatEvent) => Unit) = new Listener { 94 | @EH def on(e:PlayerChatEvent): Unit = f(e.getPlayer, e) 95 | } 96 | def OnBlockBreak(f: (Block, Player, BlockBreakEvent) => Unit) = new Listener { 97 | @EH def on(e:BlockBreakEvent): Unit = f(e.getBlock, e.getPlayer, e) 98 | } 99 | def OnBlockDamage(f: (Block, BlockDamageEvent) => Unit) = new Listener { 100 | @EH def on(e:BlockDamageEvent): Unit = f(e.getBlock, e) 101 | } 102 | def OnWeatherChange(f: WeatherChangeEvent => Unit) = new Listener { 103 | @EH def on(e:WeatherChangeEvent): Unit = f(e) 104 | } 105 | def OnPlayerInteract(f: (Player, PlayerInteractEvent) => Unit) = new Listener { 106 | @EH def on(e:PlayerInteractEvent): Unit = f(e.getPlayer, e) 107 | } 108 | def OnRightClickBlock(f: (Player, PlayerInteractEvent) => Unit) = new Listener { 109 | @EH def on(e:PlayerInteractEvent): Unit = if (e.getAction == RIGHT_CLICK_BLOCK) f(e.getPlayer, e) 110 | } 111 | def OnLeftClickBlock(f: (Player, PlayerInteractEvent) => Unit) = new Listener { 112 | @EH def on(e:PlayerInteractEvent): Unit = if (e.getAction == LEFT_CLICK_BLOCK) f(e.getPlayer, e) 113 | } 114 | def OnPlayerRightClickAir(f: (Player, PlayerInteractEvent) => Unit) = new Listener { 115 | @EH def on(e:PlayerInteractEvent): Unit = if (e.getAction == RIGHT_CLICK_AIR) f(e.getPlayer, e) 116 | } 117 | def OnPlayerLeftClickAir(f: (Player, PlayerInteractEvent) => Unit) = new Listener { 118 | @EH def on(e:PlayerInteractEvent): Unit = if (e.getAction == LEFT_CLICK_AIR) f(e.getPlayer, e) 119 | } 120 | def OnPlayerMove(f: (Player, PlayerMoveEvent) => Unit) = new Listener { 121 | @EH def on(e:PlayerMoveEvent): Unit = f(e.getPlayer, e) 122 | } 123 | def OnPlayerQuit(f: (Player, PlayerQuitEvent) => Unit) = new Listener { 124 | @EH def on(e: PlayerQuitEvent): Unit = f(e.getPlayer, e) 125 | } 126 | def OnPlayerKick(f: (Player, PlayerKickEvent) => Unit) = new Listener { 127 | @EH def on(e: PlayerKickEvent): Unit = f(e.getPlayer, e) 128 | } 129 | def OnPlayerLogin(f: (Player, PlayerLoginEvent) => Unit) = new Listener { 130 | @EH def on(e: PlayerLoginEvent): Unit = f(e.getPlayer, e) 131 | } 132 | def OnPlayerJoin(f: (Player, PlayerJoinEvent) => Unit) = new Listener { 133 | @EH def on(e: PlayerJoinEvent): Unit = f(e.getPlayer, e) 134 | } 135 | def OnArrowHitEvent(f: (Arrow, ProjectileHitEvent) => Unit) = new Listener { 136 | @EH def on(e: ProjectileHitEvent): Unit = e.getEntity match { 137 | case a: Arrow => f(a, e) 138 | case _ => 139 | } 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/NPCPlugin.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft 2 | // 3 | //import org.bukkit.Location 4 | //import ch.spacebase.npccreatures.npcs.NPCType 5 | //import ch.spacebase.npccreatures.npcs.entity.NPC 6 | //import ch.spacebase.npccreatures.NPCCreatures 7 | // 8 | ///** 9 | //* helpful urls: 10 | //* http://dev.bukkit.org/server-mods/npccreatures/ 11 | //* https://github.com/Steveice10/NPCCreatures/blob/master/src/main/java/ch/spacebase/npccreatures/npcs/NPCManager.java 12 | //* https://github.com/Steveice10/NPCCreatures/blob/master/src/main/java/ch/spacebase/npccreatures/npcs/nms/NPCNetHandler.java 13 | //* https://github.com/CitizensDev/Citizens/blob/master/src/core/net/citizensnpcs/resources/npclib/CraftNPC.java 14 | //* https://github.com/Bukkit/CraftBukkit/blob/master/src/main/java/net/minecraft/server/Entity.java 15 | //*/ 16 | //trait NPCPlugin extends CommandsPlugin { 17 | // private lazy val npcManager = 18 | // pluginManager.getPlugin("NPCCreatures").asInstanceOf[NPCCreatures].getNPCManager 19 | // def npcType = token("npc-type"){ s => Option(NPCType.getByName(s)) } 20 | // 21 | // def despawn(npc:NPC){ npcManager.despawnById(npc.getNPCId) } 22 | // private def spawn(name:String, loc: Location, npcType:NPCType): NPC = 23 | // this.npcManager.spawnNPC(name, loc,npcType) 24 | // 25 | // def human(name: String, loc:Location) = spawn(name, loc, NPCType.HUMAN) 26 | // def wolf (name: String, loc:Location) = spawn(name, loc, NPCType.WOLF) 27 | // def sheep(name: String, loc:Location) = spawn(name, loc, NPCType.SHEEP) 28 | // def villager(name: String, loc:Location) = spawn(name, loc, NPCType.VILLAGER) 29 | // def zombie(name: String, loc:Location) = spawn(name, loc, NPCType.ZOMBIE) 30 | // def spider(name: String, loc:Location) = spawn(name, loc, NPCType.SPIDER) 31 | // def skeleton(name: String, loc:Location) = spawn(name, loc, NPCType.SKELETON) 32 | // def creeper(name: String, loc:Location) = spawn(name, loc, NPCType.CREEPER) 33 | // def enderman(name: String, loc:Location) = spawn(name, loc, NPCType.ENDERMAN) 34 | // def blaze(name: String, loc:Location) = spawn(name, loc, NPCType.BLAZE) 35 | // def magmacube(name: String, loc:Location) = spawn(name, loc, NPCType.MAGMACUBE) 36 | // def pigzombie(name: String, loc:Location) = spawn(name, loc, NPCType.PIGZOMBIE) 37 | // def cavespider(name: String, loc:Location) = spawn(name, loc, NPCType.CAVESPIDER) 38 | // def slime(name: String, loc:Location) = spawn(name, loc, NPCType.SLIME) 39 | // def silverfish(name: String, loc:Location) = spawn(name, loc, NPCType.SILVERFISH) 40 | // def snowman(name: String, loc:Location) = spawn(name, loc, NPCType.SNOWMAN) 41 | // def pig(name: String, loc:Location) = spawn(name, loc, NPCType.PIG) 42 | // def chicken(name: String, loc:Location) = spawn(name, loc, NPCType.CHICKEN) 43 | // def cow(name: String, loc:Location) = spawn(name, loc, NPCType.COW) 44 | // def squid(name: String, loc:Location) = spawn(name, loc, NPCType.SQUID) 45 | // def mooshroom(name: String, loc:Location) = spawn(name, loc, NPCType.MOOSHROOM) 46 | // def enderdragon(name: String, loc:Location) = spawn(name, loc, NPCType.ENDERDRAGON) 47 | // def ghast(name: String, loc:Location) = spawn(name, loc, NPCType.GHAST) 48 | // def giant(name: String, loc:Location) = spawn(name, loc, NPCType.GIANT) 49 | // def ocelot(name: String, loc:Location) = spawn(name, loc, NPCType.OCELOT) 50 | // def irongolem(name: String, loc:Location) = spawn(name, loc, NPCType.IRONGOLEM) 51 | // 52 | // def spawn(id:Long, loc: Location, breed: String): NPC = (breed.toLowerCase match { 53 | // case "wolves" | "wolf" => wolf _ 54 | // case "sheep" => sheep _ 55 | // case "villagers" | "villager" => villager _ 56 | // case "zombies" | "zombie" => zombie _ 57 | // case "spiders" | "spider" => spider _ 58 | // case "skeletons" | "skeleton" => skeleton _ 59 | // case "creepers" | "creeper" => creeper _ 60 | // case "endermen" | "enderman" => enderman _ 61 | // case "blazes" | "blaze" => blaze _ 62 | // case "magmacubes" | "magmacube" => magmacube _ 63 | // case "pigzombies" | "pigzombie" => pigzombie _ 64 | // case "cavespiders" | "cavespider" => cavespider _ 65 | // case "slimes" | "slime" => slime _ 66 | // case "silverfish" => silverfish _ 67 | // case "snowmen" | "snowman" => snowman _ 68 | // case "pigs" | "pig" => pig _ 69 | // case "chickens" | "chicken" => chicken _ 70 | // case "cows" | "cow" => cow _ 71 | // case "squids" | "squid" => squid _ 72 | // case "mooshrooms" | "mooshroom" => mooshroom _ 73 | // case "ghasts" | "ghast" => ghast _ 74 | // case "giants" | "giant" => giant _ 75 | // case "ocelots" | "ocelot" => ocelot _ 76 | // case "irongolems" | "irongolem" | "golems" | "golem" => irongolem _ 77 | // case "enderdragons" | "enderdragon" | "dragons" | "dragon" => enderdragon _ 78 | // case _ => human _ 79 | // })(id.toString, loc) 80 | //} 81 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/PlayerState.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.entity.Player 4 | import collection.mutable.Map 5 | 6 | /** 7 | * Wrapper functions around a collection.mutable.Map[Player, T] 8 | * @tparam T the type of the state stored for the player 9 | */ 10 | trait PlayerState[T] { 11 | /** 12 | * The default value that gets stored in the Map 13 | * If None, the Map will have no default value. 14 | */ 15 | val default: Option[T] = None 16 | 17 | lazy val state = default.fold(Map[Player, T]())(t => Map[Player, T]().withDefaultValue(t)) 18 | 19 | /** 20 | * Get the state for the player 21 | * Unsafe, unless you specified a default 22 | * @param p 23 | * @return 24 | */ 25 | def apply(p: Player): T = state(p) 26 | 27 | /** 28 | * Get the state for the player, if it exists. 29 | */ 30 | def get(p: Player): Option[T] = state.get(p) 31 | 32 | /** 33 | * Set the state for the given player 34 | * @param pt 35 | * @return 36 | */ 37 | def += (pt: (Player, T)): T = { state += (pt._1 -> pt._2); pt._2 } 38 | 39 | /** 40 | * Delete the state for the given player, 41 | * and get back the state that was deleted. 42 | * @param p 43 | * @return 44 | */ 45 | def -= (p: Player): T = { val t = this(p); state -= p; t } 46 | 47 | /** 48 | * Delete the state for the given player, 49 | * and get back the state that was deleted, if it was there. 50 | */ 51 | def deletePlayerStateMaybe(p: Player): Option[T] = { val t = get(p); state -= p; t } 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/ScalaEnrichment.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import java.io.File 4 | import io.Source 5 | 6 | object ScalaEnrichment extends ScalaEnrichment 7 | /** 8 | * Adds a bunch of missing functions to Scala classes. 9 | */ 10 | trait ScalaEnrichment { 11 | 12 | /** 13 | * Just unit. 14 | */ 15 | val unit = () 16 | 17 | /** 18 | * Randomly, return true or false. 19 | */ 20 | def randomBoolen = math.random > .5 21 | 22 | /** 23 | * Allows for F# style pipelining 24 | * x |> f is the same as f(x). 25 | * TODO: double check that x |> f |> g is the same as g(f(x)). 26 | */ 27 | implicit class RichT[T](t:T){ 28 | def |> [U](f: T => U) = f(t) 29 | } 30 | 31 | /** 32 | * Implicit conversion to Runnable. 33 | */ 34 | implicit def byNameToRunnable(f: => Unit) = new Runnable { override def run = f } 35 | 36 | def spawn(f: => Unit): Unit = new Thread(f).start() 37 | 38 | /** 39 | * Enrich a Function1. 40 | */ 41 | implicit class RichFunction1[A,B,R](f: A => R) { 42 | def of(a:A): R = f(a) 43 | } 44 | 45 | /** 46 | * Enrich a Function1 that returns a Function1. 47 | */ 48 | implicit class RichFunction1Function1[A,B,R](f: A => B => R) { 49 | /** 50 | * Turn A -> B -> R into B -> A -> R 51 | */ 52 | def flip: B => A => R = b => a => f(a)(b) 53 | } 54 | 55 | /** 56 | * Enrich a Function2 57 | */ 58 | implicit class RichFunction2[A,B,R](f: (A, B) => R) { 59 | /** 60 | * Turn (A -> B) -> R into (B -> A) -> R 61 | */ 62 | def flip: (B, A) => R = (b, a) => f(a, b) 63 | } 64 | 65 | /** 66 | * Enrich an Option 67 | */ 68 | implicit class RichOption[T](o: Option[T]){ 69 | /** 70 | * Fold with the argument order reversed, because sometimes its nice to put the 71 | * success case first, and the failure case last. 72 | */ 73 | def flipFold[B](f: T => B)(ifEmpty: => B) = o.fold(ifEmpty)(f) 74 | } 75 | 76 | /** 77 | * Alias for identity. 78 | */ 79 | def id[T](t:T) = identity(t) 80 | 81 | /** 82 | * Adds and and or functions to Booleans. 83 | */ 84 | implicit class RichBoolean(b1:Boolean) { 85 | def or (b2: => Boolean) = b1 || b2 86 | def and(b2: => Boolean) = b1 && b2 87 | def toOption[T](t: => T) = if (b1) Some(t) else None 88 | } 89 | 90 | /** 91 | * Adds isEven, isOdd to Ints. 92 | */ 93 | implicit class RichInt(i: Int){ 94 | def isEven = i % 2 == 0 95 | def isOdd = ! isEven 96 | } 97 | 98 | /** 99 | * Add some fun functions to File. 100 | */ 101 | implicit class RichFile(f:File){ 102 | def child(name:String): File = new File(f, name) 103 | def slurp: String = Source.fromFile(f).getLines().mkString("\n") 104 | /** 105 | * Tries to find a file. If it exists, returns Some(file). If not, None. 106 | */ 107 | def toOption(name:String): Option[File] = { 108 | val f = new File(name) 109 | if(f.exists) Some(f) else None 110 | } 111 | } 112 | 113 | implicit class RichInputStream(input: java.io.InputStream) { 114 | /** 115 | * Read all the data from the given InputStream 116 | * and copy it to the given OutputStream. 117 | * @return the number of bytes read and sent 118 | */ 119 | def copyTo(output:java.io.OutputStream, 120 | defaultBufferSize:Int=(256), 121 | closeInputStream:Boolean=true): Long = try { 122 | val buffer = new Array[Byte](defaultBufferSize) 123 | var count = 0L 124 | var n = input.read(buffer) 125 | while (n != -1) { 126 | output.write(buffer, 0, n) 127 | count += n 128 | n = input.read(buffer) 129 | } 130 | count 131 | } finally if (closeInputStream) input.close() 132 | 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/ScalaPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.Server 4 | import org.bukkit.entity.Player 5 | import org.bukkit.event.{Listener, Event} 6 | import org.bukkit.plugin.java.JavaPlugin 7 | import util.Try 8 | import java.util.logging.{Level, Logger} 9 | import javax.persistence.PersistenceException 10 | 11 | /** 12 | * The base class that helps make writing Bukkit plugins vastly easier. 13 | * However, it's unlikely that you'll subclass ScalaPlugin directly. It's 14 | * far more likely that you'll subclass com.joshcough.minecraft.CommandsPlugin, 15 | * com.joshcough.minecraft.ListenersPlugin, or both. 16 | */ 17 | abstract class ScalaPlugin extends JavaPlugin with BukkitEnrichment { scalaPlugin => 18 | 19 | lazy val log = Logger.getLogger("Minecraft") 20 | 21 | // setup stuff 22 | override def onEnable: Unit = { 23 | super.onEnable 24 | this.saveDefaultConfig 25 | setupDatabase 26 | logInfo(s"$name enabled!") 27 | } 28 | 29 | override def onDisable: Unit = { super.onDisable; logInfo(s"$name disabled!") } 30 | 31 | /** 32 | * A list of dependencies that this plugin depends on. 33 | * JcdcPluginFactory is automatically included, which contains Scala, Clojure, and 34 | * all of the classes in com.joshcough.minecraft. 35 | * See http://wiki.bukkit.org/Plugin_YAML for more info 36 | */ 37 | def dependencies: List[String] = Nil 38 | 39 | // the ScalaLibPlugin provides Scala at runtime. 40 | // the ScalaPluginAPI provides com.joshcough.minecraft.* classes at runtime 41 | private val mandatoryDependencies = List("ScalaLibPlugin", "ScalaPluginAPI") 42 | 43 | /** 44 | * A list of all the soft dependencies for this plugin. 45 | * See http://wiki.bukkit.org/Plugin_YAML for more info 46 | */ 47 | def softDependencies: List[String] = Nil 48 | 49 | // TODO: is there a real yml data type i could use? 50 | def configs: Map[String, String] = Map() 51 | 52 | /** 53 | * Classes that want to use a database should override this def, providing 54 | * all of the Entity classes. See WarpPlugin in examples. 55 | */ 56 | def dbClasses: List[Class[_]] = Nil 57 | // this is here just so subclasses dont have to use java.util.ArrayList. 58 | override def getDatabaseClasses = new java.util.ArrayList[Class[_]](){ dbClasses.foreach(add) } 59 | // this is horrible bukkit nonsense that every plugin must do if it wants to use the database. 60 | private def setupDatabase: Unit = 61 | if(dbClasses.nonEmpty) 62 | // this somehow forces attempting to initialize the database 63 | try getDatabase.find(dbClasses.head).findRowCount 64 | // and if it throws... that means you haven't yet initialized the db, 65 | // and you need to call installDLL... 66 | // really, this is just crap. happy to hide it from any users. 67 | catch{ case e: PersistenceException => logTask("Installing DB"){ installDDL() } } 68 | 69 | /** 70 | * Generates the plugin.yml contents for this plugin. 71 | * See http://wiki.bukkit.org/Plugin_YAML for more info 72 | * @param author the author of the plugin 73 | * @param version the version of the plugin 74 | **/ 75 | def yml(author:String, version: String) = List( 76 | "name: " + this.name, 77 | "main: " + this.getClass.getName, 78 | "author: " + author, 79 | "version: " + version, 80 | "database: " + (this.dbClasses.size > 0), 81 | "depend: [" + (mandatoryDependencies ++ this.dependencies).mkString(", ") + "]", 82 | "softdepend: [" + this.softDependencies.mkString(", ") + "]" 83 | ).mkString("\n") 84 | 85 | /** 86 | * Writes out the plugin.yml file, and config.yml. 87 | * @param author the author of the plugin 88 | * @param version the version of the plugin 89 | */ 90 | def writeYML(author: String, version: String, outputDir: String = "."): Unit = { 91 | val resources = new java.io.File(outputDir) 92 | resources.mkdirs 93 | def write(contents: String, filename:String): Unit = { 94 | val f = new java.io.FileWriter(new java.io.File(resources, filename)) 95 | f.write(contents) 96 | f.close 97 | } 98 | write(yml(author, version), "plugin.yml") 99 | write(configs.toList.map{ case (k, v) => s"$k: $v" }.mkString("\n"), "config.yml") 100 | } 101 | 102 | /** 103 | * Broadcast a message to the world. 104 | * The name of the plugin is prepended to the given message, like so: 105 | * [plugin-name] - message 106 | */ 107 | def broadcast(message:String): Unit = server.broadcastMessage(s"[$name] - $message") 108 | 109 | /** 110 | * Log the given message at INFO level. 111 | */ 112 | def logInfo(message:String): Unit = logMessage(Level.INFO, message) 113 | 114 | /** 115 | * Log the given message at WARNING level. 116 | */ 117 | def logWarning(message:String): Unit = logMessage(Level.WARNING, message) 118 | 119 | /** 120 | * Log the given exception at SEVERE level. 121 | */ 122 | def logError(e:Throwable): Unit = logMessage(Level.SEVERE, e.getMessage + e.getStackTraceString) 123 | 124 | private def logMessage(level: Level, message: String): Unit = 125 | log.log(level, s"[$name] - $message") 126 | 127 | /** 128 | * Log around the given task like so: 129 | * 'Starting - message' 130 | * f 131 | * 'Finished - message' 132 | */ 133 | def logTask[T](message:String)(f: => T): T = { 134 | logInfo(s"Starting: $message"); val t = f; logInfo(s"Finished: $message"); t 135 | } 136 | 137 | 138 | // Various other little helper functions. 139 | def name = Try(this.getDescription.getName).getOrElse(this.getClass.getSimpleName) 140 | def server: Server = getServer 141 | def pluginManager = getServer.getPluginManager 142 | def fire(e:Event): Unit = server.getPluginManager.callEvent(e) 143 | def registerListener(listener:Listener): Unit = pluginManager.registerEvents(listener, this) 144 | 145 | // task stuff: 146 | private lazy val scheduler = server.getScheduler 147 | 148 | case class Task(id:Int) 149 | 150 | def scheduleSyncTask(task: => Unit): Task = 151 | Task(scheduler.scheduleSyncDelayedTask(this, task)) 152 | 153 | def scheduleSyncDelayedTask(initialDelay: Long)(task: => Unit): Task = 154 | Task(scheduler.scheduleSyncDelayedTask(this, task, initialDelay)) 155 | 156 | def scheduleSyncRepeatingTask(period: Long)(task: => Unit): Task = 157 | Task(scheduler.scheduleSyncRepeatingTask(this, task, 0L, period)) 158 | 159 | def scheduleSyncRepeatingTask(initialDelay: Long, period: Long)(task: => Unit): Task = 160 | Task(scheduler.scheduleSyncRepeatingTask(this, task, initialDelay, period)) 161 | 162 | def cancelTask(t: Task) = scheduler cancelTask t.id 163 | 164 | case class PlayerTasks(cancelOnExit: Boolean = true) extends PlayerState[Seq[Task]] { self => 165 | override val default: Option[Seq[Task]] = Some(Nil) 166 | 167 | registerListener(Listeners.OnPlayerQuit((p, _) => if(cancelOnExit) p.cancelAll)) 168 | 169 | implicit class PlayerWithTaskFunctions(p:Player){ 170 | private def addTask(t: Task): Task = { self += (p -> (self(p) :+ t)); t } 171 | 172 | def scheduleSyncTask(task: => Unit): Task = addTask(scalaPlugin.scheduleSyncTask(task)) 173 | 174 | def scheduleSyncRepeatingTask(initialDelay: Long, period: Long)(task: => Unit): Task = 175 | addTask(scalaPlugin.scheduleSyncRepeatingTask(initialDelay, period)(task)) 176 | 177 | def cancelTask(t: Task): Unit = { 178 | scheduler cancelTask t.id 179 | self += (p -> self(p).filter(_ != t)) 180 | } 181 | def cancelAll: Unit = { 182 | logInfo(s"canceling all tasks for: $p") 183 | (self -= p) foreach { t => 184 | logInfo(s"canceling: $t") 185 | scheduler cancelTask t.id 186 | } 187 | } 188 | } 189 | } 190 | 191 | /** 192 | * Invokes a command programmatically. 193 | */ 194 | def runCommand(p: Player, commandName: String, args: Seq[String]) = { 195 | p ! s"$name running: $commandName ${args.mkString(" ")}" 196 | onCommand(p, getCommand(commandName), commandName, args.toArray) 197 | } 198 | } 199 | 200 | /** 201 | * This plugin and this code is not intended for use. 202 | * It is just an empty plugin that is required for turning this library 203 | * into a plugin, so that the API and Scala can be on the classpath for 204 | * plugins that want to use this API. 205 | */ 206 | class ScalaPluginAPI extends org.bukkit.plugin.java.JavaPlugin { 207 | override def onEnable : Unit = {} 208 | override def onDisable : Unit = {} 209 | } 210 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/UndoState.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import collection.immutable.Stack 4 | import ScalaEnrichment.RichBoolean 5 | 6 | case class UndoState[T, U](undoStack: Stack[T] = Stack(), redoStack: Stack[U] = Stack()){ 7 | 8 | override def toString = 9 | s"UndoState(undoStack=(${undoStack.mkString(",")}) redoState=(${redoStack.toList}))" 10 | 11 | /** 12 | * if you make a change, it goes on top of the undo stack 13 | * and the redo stack is cleared. 14 | */ 15 | def newChange(t: T): UndoState[T, U] = copy(undoStack = undoStack.push(t), redoStack = Stack[U]()) 16 | 17 | /** 18 | * undo: take off top of undo stack and put onto redo stack 19 | */ 20 | def undo(f: T => U): Option[UndoState[T, U]] = undoStack.nonEmpty.toOption( 21 | copy(undoStack.pop, redoStack.push(f(undoStack.top))) 22 | ) 23 | 24 | /** 25 | * redo: take off top of redo stack and put onto undo stack 26 | */ 27 | def redo(f: U => T): Option[UndoState[T, U]] = redoStack.nonEmpty.toOption( 28 | copy(undoStack.push(f(redoStack.top)), redoStack.pop) 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/scala/com/joshcough/minecraft/YMLGenerator.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import ParserCombinators._ 4 | 5 | // A helper object I use to auto generate all my plugin.yml files. 6 | object YMLGenerator { 7 | 8 | val argsParser = 9 | anyString.named("className") ~ 10 | anyString.named("author") ~ 11 | anyString.named("version") ~ 12 | anyString.named("outputDir") ~ 13 | eof 14 | 15 | def create[T](className: String) = Class.forName(className).newInstance.asInstanceOf[T] 16 | 17 | def generateYml(className: String, author: String, version: String, outputDir: String) = 18 | create[ScalaPlugin](className).writeYML(author, version, outputDir) 19 | 20 | def main(args: Array[String]): Unit = { 21 | argsParser(args.toList).extract { 22 | case className ~ author ~ version ~ outputDir ~ _ => generateYml(className, author, version, outputDir) 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/CubeTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck._ 4 | import org.scalacheck.Prop._ 5 | import Gen._ 6 | import Arbitrary.arbitrary 7 | import Cube._ 8 | import ScalaEnrichment.id 9 | 10 | /** 11 | * Generate cubes, possibly up to the maximum sized cube. 12 | */ 13 | trait CubeGenerators { 14 | type C = Cube[Point] 15 | implicit val genCoor = for { 16 | x <- arbitrary[Int]; y <- arbitrary[Int]; z <- arbitrary[Int] 17 | } yield Point(x, y, z) 18 | implicit val genCube: Gen[C] = for { c1 <- genCoor; c2 <- genCoor } yield Cube(c1, c2)(identity) 19 | implicit val cubes = Arbitrary(genCube) 20 | } 21 | 22 | /** 23 | * Generate cubes with: 24 | * max x, y, and z no greater than 50 25 | * min x, y, and z no less than -50 26 | */ 27 | trait SmallCubeGenerators { self: Properties => 28 | type C = Cube[Point] 29 | val smallInteger = Gen.choose(-50,50) 30 | val genSmallCube: Gen[C] = for { 31 | x1 <- smallInteger; y1 <- smallInteger; z1 <- smallInteger 32 | x2 <- smallInteger; y2 <- smallInteger; z2 <- smallInteger 33 | } yield Cube(Point(x1, y1, z1), Point(x2, y2, z2))(identity) 34 | implicit val smallCubes = Arbitrary(genSmallCube) 35 | } 36 | 37 | abstract class CubeTestBase(name: String) extends Properties(name) with TestHelpers { 38 | def idCube(p1: Point, p2: Point) = Cube(p1, p2)(id) 39 | def toList(c: Cube[Point]) = c.toStream.toList 40 | def run(c: Cube[Point]) = c.toZippedStream.toList 41 | } 42 | 43 | /** 44 | * This object is for testing general properties on cubes, 45 | * where those properties are simple (O(1), usually), and calculating them doesn't 46 | * require traversing the whole cube. 47 | */ 48 | object BigCubeTests extends CubeTestBase("Cube Tests Awesome") with CubeGenerators { 49 | test("cubes size > 0")(forAll{ (c:C) => c.size > 0 }) 50 | } 51 | 52 | /** 53 | * This object is for testing operations on random cubes, 54 | * where those operations must traverse the cube. 55 | * That is why we use SmallCubeGenerators, so we don't generate ridiculously huge cubes 56 | */ 57 | object SmallCubeTests extends CubeTestBase("Cube Tests Awesome") with SmallCubeGenerators { 58 | 59 | test("paste then mirror y same as mirror y then paste")(forAll{ (c:C) => 60 | run(c.translateTo(Point(0, 10, 0)).mirrorY) ?= run(c.mirrorY.translateTo(Point(0, 10, 0))) 61 | }) 62 | 63 | test("mirror x y z, same as mirror z y x")(forAll{ (c:C) => 64 | run(c.mirrorX.mirrorY.mirrorZ) ?= run(c.mirrorZ.mirrorY.mirrorX) 65 | }) 66 | 67 | test("mirror x x, same as identity")(forAll{ (c:C) => run(c.mirrorX.mirrorX) ?= run(c) }) 68 | 69 | test("grow then shrink, same as identity")(forAll{ (c:C) => 70 | run(c.grow(5,6,7).shrink(5,6,7)) ?= run(c) 71 | }) 72 | 73 | test("shift up, shift down, same as identity")(forAll{ (c:C, i: Int) => 74 | run(c.shiftUp(i).shiftDown(i)) ?= run(c) 75 | }) 76 | 77 | test("shift by i, shift by -i, same as identity")(forAll{ (c:C, i: Int) => 78 | run(c.shiftX(i).shiftX(-i).shiftY(i).shiftY(-i).shiftZ(i).shiftZ(-i)) ?= run(c) 79 | }) 80 | } 81 | 82 | 83 | /** 84 | * Literal tests that don't use the generators. 85 | */ 86 | object LiteralCubeTests extends CubeTestBase("Cube Tests") { 87 | 88 | val c0 = idCube(Point(0,0,0),Point(0,0,0)) 89 | val c = idCube(Point(0,0,0),Point(10,10,10)) 90 | 91 | trait P { def apply(c: Point): Int } 92 | case object X extends P { def apply(c: Point): Int = c.x } 93 | case object Y extends P { def apply(c: Point): Int = c.y } 94 | case object Z extends P { def apply(c: Point): Int = c.z } 95 | 96 | test("ap") { 97 | Cube.ap( 98 | Cube(Point(0,0,0),Point(0,0,0))(c => (p:P) => p(c)), 99 | Cube[P](Point(0,0,0),Point(0,0,0))(c => X) 100 | )(Point(0,0,0)) ?= 0 101 | } 102 | 103 | test("size 0"){ c0.size ?= 1 } 104 | 105 | test("simple") { c ?= idCube(Point(0,0,0),Point(10,10,10)) } 106 | test("expand") { c.expand (1) ?= idCube(Point(-1,-1,-1),Point(11,11,11)) } 107 | test("expandXY") { c.expandXZ(1) ?= idCube(Point(-1,0,-1),Point(11,10,11)) } 108 | 109 | test("shrink all the way") { 110 | c.shrink(5,5,5) ?= idCube(Point(5,5,5),Point(5,5,5)) 111 | c.shrink(100,100,100) ?= idCube(Point(5,5,5),Point(5,5,5)) 112 | idCube(Point(11,11,11),Point(0,0,0)).shrink(6,6,6) ?= idCube(Point(6,6,6),Point(5,5,5)) 113 | } 114 | 115 | test("shrink more") { c.shrink(2,3,4) ?= idCube(Point(2,3,4),Point(8,7,6)) } 116 | 117 | test("grow") { c.grow(2,3,4) ?= idCube(Point(-2,-3,-4),Point(12,13,14)) } 118 | 119 | // the max - min check here makes sure we don't wrap around to a negative int. 120 | test("shrink")(forAll{ (max:Int,min:Int) => (max >= min && max - min > 1) ==> { 121 | val c = idCube(Point(max,max,max), Point(min,min,min)) 122 | c.size > c.shrink(1,1,1).size 123 | }}) 124 | } 125 | 126 | /** 127 | * Literal tests on mirroring that don't use the generators. 128 | */ 129 | object LiteralCubeMirroringTests extends CubeTestBase("Cube Mirroring Tests") { 130 | 131 | val cx = idCube(Point(0,0,0),Point(3,0,0)) 132 | val cy = idCube(Point(0,0,0),Point(0,3,0)) 133 | val cz = idCube(Point(0,0,0),Point(0,0,3)) 134 | 135 | test("normal x") { toList(cx) ?= List(Point(0,0,0),Point(1,0,0),Point(2,0,0),Point(3,0,0)) } 136 | test("normal y") { toList(cy) ?= List(Point(0,0,0),Point(0,1,0),Point(0,2,0),Point(0,3,0)) } 137 | test("mirrorX") { toList(cx.mirrorX) ?= List(Point(3,0,0),Point(2,0,0),Point(1,0,0),Point(0,0,0)) } 138 | test("mirrorY") { toList(cy.mirrorY) ?= List(Point(0,3,0),Point(0,2,0),Point(0,1,0),Point(0,0,0)) } 139 | test("mirrorZ") { toList(cz.mirrorZ) ?= List(Point(0,0,3),Point(0,0,2),Point(0,0,1),Point(0,0,0)) } 140 | 141 | test("paste y") { 142 | run(cy.translateTo(Point(5, 0, 0))) ?= List( 143 | (Point(5,0,0),Point(0,0,0)), (Point(5,1,0),Point(0,1,0)), 144 | (Point(5,2,0),Point(0,2,0)), (Point(5,3,0),Point(0,3,0)) 145 | ) 146 | } 147 | 148 | test("paste y twice") { 149 | run(cy.translateTo(Point(0, 10, 0)).translateTo(Point(0, 20, 0))) ?= List( 150 | (Point(0,20,0),Point(0,0,0)), 151 | (Point(0,21,0),Point(0,1,0)), 152 | (Point(0,22,0),Point(0,2,0)), 153 | (Point(0,23,0),Point(0,3,0)) 154 | ) 155 | } 156 | 157 | test("paste then mirror y") { 158 | run(cy.translateTo(Point(0, 10, 0)).mirrorY) ?= List( 159 | (Point(0,10,0),Point(0,3,0)), 160 | (Point(0,11,0),Point(0,2,0)), 161 | (Point(0,12,0),Point(0,1,0)), 162 | (Point(0,13,0),Point(0,0,0)) 163 | ) 164 | } 165 | 166 | test("mirror then paste y") { 167 | run(cy.mirrorY.translateTo(Point(0, 10, 0))) ?= List( 168 | (Point(0,10,0),Point(0,3,0)), 169 | (Point(0,11,0),Point(0,2,0)), 170 | (Point(0,12,0),Point(0,1,0)), 171 | (Point(0,13,0),Point(0,0,0)) 172 | ) 173 | } 174 | } 175 | 176 | object ShrinkageTests extends CubeTestBase("Past failures for Cubes") { 177 | test("cube of size 1 wont shrink.") { 178 | idCube(Point(3,0,0), Point(3,0,0)).shrink(5,0,0) ?= idCube(Point(3,0,0), Point(3,0,0)) 179 | } 180 | test("cube of size 2 wont shrink.") { 181 | idCube(Point(4,0,0), Point(3,0,0)).shrink(1,0,0) ?= idCube(Point(4,0,0), Point(3,0,0)) 182 | } 183 | test("shrink test 3") { 184 | idCube(Point(6,0,0), Point(3,0,0)).shrink(5,0,0) ?= idCube(Point(5,0,0), Point(4,0,0)) 185 | } 186 | test("shrink test 4") { 187 | idCube(Point(6,0,0), Point(3,0,0)).shrink(1,0,0) ?= idCube(Point(5,0,0), Point(4,0,0)) 188 | } 189 | test("shrink test 5") { 190 | idCube(Point(6,0,0), Point(3,0,0)).shrink(2,0,0) ?= idCube(Point(5,0,0), Point(4,0,0)) 191 | } 192 | } -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/MinecraftParsersTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | import org.bukkit.entity.EntityType 6 | import org.bukkit.Material 7 | import Material._ 8 | 9 | object MinecraftParsersTests extends Properties("MinecraftParserTests") with TestHelpers { 10 | 11 | import MinecraftParsers._ 12 | 13 | for(m <- Material.values) test(m.toString) { 14 | (material(m.name).get ?= m) && (material(m.toString.toLowerCase).get ?= m) 15 | // getId is deprecated, and is now broken in bukkit for some ids 16 | // for now it will still work in most places for plugin users, but 17 | // testing it causes test failures. oh well. 18 | // && (material(m.getId.toString).get ?= m) 19 | } 20 | 21 | for(e <- EntityType.values) test(e.toString) { 22 | (entity(e.name).get ?= e) && (entity(e.toString.toLowerCase).get ?= e) 23 | } 24 | 25 | test("(material or eof)(gold_ore)") { 26 | (material or eof)("gold_ore").get.left.get ?= GOLD_ORE 27 | } 28 | 29 | test("(material or eof)(Nil)") { 30 | (material or eof)(Nil).get.isRight 31 | } 32 | 33 | test("(material or eof)(werersd)") { 34 | val res = (material or eof)("werersd").fold(id)((p,r) => 35 | "parser worked with bogus material type: werersd, but shouldnt have." 36 | ) 37 | res ?= "invalid material-type: werersd or expected eof, but got: werersd" 38 | } 39 | 40 | test("((material or eof) <~ eof)(dirt 7)") { 41 | val res = ((material or eof) <~ eof)("dirt 7").fold(id)((_,_) => 42 | "parser worked, but shouldnt have." 43 | ) 44 | res ?= "expected eof, but got: 7" 45 | } 46 | } -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/ParserCombinatorDemo.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | object ParserCombinatorDemo { 4 | import com.joshcough.minecraft.ParserCombinators._ 5 | def run[T](p: Parser[T], args:String): ParseResult[T] = p(args) 6 | // demo 7 | run(int, "5") 8 | run(int, "ewrer") 9 | run(int ~ int, "5 6") 10 | run(int ~ int, "5 qweqwe") 11 | run(int ~ anyString, "5 qweqwe") 12 | run(int ~ anyString, "5 qweqwe wfwfwef") 13 | run(bool or int , "true") 14 | run(bool or int , "7") 15 | run(bool or int , "qweqw") 16 | run("test", "test") 17 | run(int.*, "5 7 8 9") 18 | run(bool.+, "true") 19 | run(bool.+, "true false true") 20 | run(bool.+, "true false true qwewe") 21 | run(int ^^ (x => x * x), "7") 22 | run(int ~ "*" ~ int ^^^ 42, "6 * 9") 23 | run(int.? ~ "hi", "hi") 24 | run(int.? ~ "hi", "6 hi") 25 | } 26 | -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/ParserCombinatorTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | 6 | object ParserCombinatorTests extends Properties("ParserCombinatorTests") with ParserCombinators with TestHelpers { 7 | 8 | test("strings")(forAll { (s:String) => stringToParser(s)(List(s)).get ?= s }) 9 | 10 | property("ints") = forAll { (i:Int) => int(List(i.toString)).get ?= i } 11 | 12 | property("any") = forAll { (i: Int, s: String, l:List[String]) => 13 | success((i, s))(l) ?= Success((i, s), l) 14 | } 15 | 16 | property("odd ok") = forAll { (i:Int) => (i % 2 == 1) ==> 17 | (oddNum(List(i.toString)).get ?= i) 18 | } 19 | 20 | property("even ok") = forAll { (i:Int) => (i % 2 == 0) ==> 21 | (evenNum(List(i.toString)).get ?= i) 22 | } 23 | 24 | property("odd bad") = forAll { (i:Int) => (i % 2 == 0) ==> 25 | (oddNum(List(i.toString)) ?= Failure(s"invalid odd-int: $i")) 26 | } 27 | 28 | property("even bad") = forAll { (i:Int) => (i % 2 == 1) ==> 29 | (evenNum(List(i.toString)) ?= Failure(s"invalid even-int: $i")) 30 | } 31 | 32 | property("+ ok") = forAll { (is:List[Int]) => (is.size > 0) ==> 33 | (int+(is.map(_.toString)) ?= Success(is, Nil)) 34 | } 35 | 36 | property("+ on nil") = { int+(Nil) ?= Failure("expected int, but got nothing") } 37 | 38 | property("*") = forAll { (is:List[Int]) => int*(is.map(_.toString)) ?= Success(is, Nil) } 39 | 40 | property("int") = { int(List("wfwefw")) ?= Failure("invalid int: wfwefw") } 41 | 42 | property("int ~ int") = forAll { (i:Int, j: Int) => 43 | (int ~ int)(List(i.toString, j.toString)).get ?= new ~(i, j) 44 | } 45 | 46 | property("int <~ anyString") = forAll { (i:Int, s: String) => 47 | (int <~ anyString)(List(i.toString, s)).get ?= i 48 | } 49 | 50 | property("int ~> anyString") = forAll { (i:Int, s: String) => 51 | (int ~> anyString)(List(i.toString, s)).get ?= s 52 | } 53 | 54 | property("int ~> int <~ anyString") = forAll { (i:Int, j: Int, s: String) => 55 | (int ~> int <~ anyString)(List(i.toString, j.toString, s)).get ?= j 56 | } 57 | 58 | test("(eof)(Nil)") { eof(Nil) ?= Success((), Nil) } 59 | test("(eof)(\"werersd\")") { 60 | eof("werersd") ?= Failure("expected eof, but got: werersd") 61 | } 62 | test("(\"werersd\" <~ eof)('werersd')") { 63 | ("werersd" <~ eof)("werersd").get ?= "werersd" 64 | } 65 | test("(\"werersd\" ~> eof)('werersd')") { 66 | ("werersd" ~> eof)("werersd").get ?= unit 67 | } 68 | } -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/ScalaEnrichmentTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop._ 5 | import ScalaEnrichment._ 6 | 7 | object ScalaEnrichmentTests extends Properties("ScalaEnrichmentTests") with TestHelpers { 8 | test("simple") { Some("hi").flipFold(s => s + s)("fail?") ?= "hihi" } 9 | } 10 | -------------------------------------------------------------------------------- /core/src/test/scala/com/joshcough/minecraft/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop 5 | import org.scalacheck.Prop._ 6 | 7 | trait TestHelpers { self: Properties => 8 | 9 | implicit val world = TestServer.world 10 | 11 | def trying(f: => Prop) = secure { 12 | try f catch { case e: Throwable => e.printStackTrace; throw e } 13 | } 14 | 15 | def test(name:String)(f: => Prop) = property(name) = trying(f) 16 | } -------------------------------------------------------------------------------- /docs/Making Java APIs usable with Scala.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joshcough/MinecraftPlugins/8db54d9c1280a9eff2f4633139328161b172e79f/docs/Making Java APIs usable with Scala.pdf -------------------------------------------------------------------------------- /docs/bukkit.md: -------------------------------------------------------------------------------- 1 | Provides a library for writing server plugins in Scala. The plugins are vastly simpler to write than using the original Bukkit API in Java. 2 | 3 | **_Update_** - This project now provides two plugins: 4 | 5 | * scala-library-plugin-2.10.3.jar 6 | * scala-plugin-api_2.10-0.3.3.jar 7 | 8 | The first plugin provides Scala 2.10.3, so that plugins that depend on it can have scala on the classpath. 9 | 10 | The second is an entire Scala Minecraft Server Plugin API that cleans up the Bukkit API very nicely, and allows for writing clean, declarative, immutable, and fun plugins. 11 | 12 | The source for the project can be found here: 13 | [https://github.com/joshcough/MinecraftPlugins](https://github.com/joshcough/MinecraftPlugins/scala). 14 | 15 | ## Examples 16 | 17 | ### Listener example: LightningArrows 18 | 19 | The following example demonstrates how to use listeners in Scala. 20 | This plugin strikes an entity (any player or mob) with lightning if that entity is hit with an arrow: 21 | 22 | import com.joshcough.minecraft.Listeners._ 23 | import org.bukkit.entity.EntityType.ARROW 24 | 25 | class LightningArrows extends ListeningFor(OnEntityDamageByEntity { e => 26 | if (e.getDamager isAn ARROW) e.world.strikeLightning(e.loc) 27 | }) 28 | 29 | ### Commands example: Shock 30 | 31 | The following example demonstrates how to write commands in Scala. 32 | It has one command called "shock" which takes a player as input, and shocks that player with lightning. 33 | 34 | import com.joshcough.minecraft.CommandPlugin 35 | 36 | class Shock extends CommandPlugin { 37 | val command = Command("shock", "shock a player", player){ 38 | case (you, them) => them.shock 39 | } 40 | } 41 | 42 | ## Other features 43 | 44 | * plugin.yml is automatically generated for you. 45 | * Advanced cube api for 3D world manipulation 46 | * Cleaner database connectivity 47 | * Cleaner logging api 48 | 49 | ## Getting Started 50 | 51 | A complete guide to getting started writing Scala plugins can be found here: 52 | [https://github.com/joshcough/MinecraftPlugins/wiki/Getting%20Started](https://github.com/joshcough/MinecraftPlugins/wiki/Getting%20Started) 53 | 54 | ## More examples 55 | 56 | Many, many more examples can be found here: 57 | [https://github.com/joshcough/MinecraftPlugins/tree/master/examples/src/main/scala/com/joshcough/minecraft/scala/examples](https://github.com/joshcough/MinecraftPlugins/tree/master/scala/examples/src/main/scala/com/joshcough/minecraft/examples), 58 | including Scala versions of WorldEdit, Multi Player Commands (a plugin that supports many of the 59 | same commands as Single Player Commands), Warp, and more. 60 | 61 | ## Scaladoc 62 | 63 | The Scaladoc for the project can be found here: 64 | 65 | [http://joshcough.github.com/MinecraftPlugins/scaladoc](http://joshcough.github.com/MinecraftPlugins/scaladoc) -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # 0.3.4 2 | 3 | * Upgraded to Bukkit 1.7.2-R0.2 4 | * Upgraded to Scala 2.10.4 and 2.11.6 5 | 6 | # 0.3.0 7 | 8 | ## Summary 9 | 10 | * Upgraded to Bukkit 1.4.7-R0.1 11 | * Upgraded to Scala 2.10.0 12 | 13 | ## Core Code Changes 14 | 15 | * Extensive Cube API additions 16 | * Changed Cube to be polymorphic 17 | * Got rid of Coor, and now just have a type alias Point = (Int, Int, Int) 18 | * Added Applicative Functor for Cube 19 | * Made shift operations be transformations on cubes. 20 | * Removed MinecraftCube, and placed its functions elsewhere. 21 | renamed Cube to CubeState, 22 | * Added some operators to PlayerState 23 | 24 | ## Bug Fixes/Example/Other Code Changes 25 | 26 | * World Edit changes 27 | * Implemented undo and redo, and made all commands work with it. 28 | * Properly reporting the number of blocks changed. 29 | * Implemented paste. It is monumental. 30 | * Renamed the /change command to /replace 31 | * Implemented flip and flipxy, which are both really fun. 32 | * Added ability to turn undo on and off. 33 | * Fixed the walls and move commands, they were broken 34 | * Fixed minor bug in #blocks-changed reporting 35 | * Fixed bugs in cube size code 36 | * Added scalacheck generators for cubes, and added lots of nice commutativity and other tests. 37 | 38 | # 0.2.2 39 | 40 | ## Summary 41 | 42 | * split out the scala-library plugin from my own code. that might seem a little funny, 43 | but the scala code will change far less than mine, and users don't need to be downloading 44 | a 10 meg file every time i make changes. 45 | * upgraded to Bukkit 1.4.6-R0.3 46 | 47 | ## Core Code Changes 48 | 49 | * added corners function to Cube 50 | * added shell function to Cube, which gives back walls, floor, and ceiling. 51 | * added randomBoolen function to ScalaEnrichment 52 | * added length, height and depth parsers (all ints) 53 | * removed WorldEditCommands, because it's really not needed. i should figure out how to call commands from another plugin anyway, and just depend on worldedit instead. 54 | * added OnPlayerQuit Listener 55 | * renamed noArguments to eof, and added aliases noArguments, nothing, empty. not sure which of them i like or if i will keep them. 56 | * added byNameToRunnable implicit conversion in ScalaEnrichment 57 | * added new Task api 58 | * added functional wrappers on scheduleSyncTask, scheduleSyncDelayedTask, scheduleSyncRepeatingTask 59 | * added PlayerTasks class that: 60 | * also provides wrappers for creating tasks 61 | * keeps track of running tasks for players. 62 | * cancels them when the player leaves the game, if desired 63 | * examples in WorldEdits 64 | 65 | ## Bug Fixes 66 | 67 | * fixed error reporting in CommandsPlugin, it was broken. 68 | * fixed ~ (the parser) description, because it was completely broken 69 | * fixed a horrible bug in Option flipFold, and added tests for it. 70 | 71 | ## Example/Other Code Changes 72 | 73 | * refactored WorldEdit to use the Cubes trait, removed WorldEditCubes, and refactored the Cubes trait a bit. Refactored Arena, and GetOffMyLawn to be current with the new Cubes changes. 74 | * added random-house command, which changes all the blocks of the house to random materials (selected from materials the user typed in) 75 | * added awesome wave command which creates a wave with length ~ height ~ material (no depth) 76 | * added crazy-walls command to worldedit, to show off the task stuff. it has your walls change every second. 77 | * updated WorldEdit with the cycle-walls command to use the new task api. the command changes walls to different materials, repeatedly until the user logs out. 78 | * fixed up build scripts by adding publish-local.sh 79 | * implemented ParserMonad in Java. puke. renamed ArgParser to just Parser in Java 80 | * simplified WorldEditDemo a bit 81 | 82 | # 0.2.1 83 | 84 | ## Core Code Changes 85 | 86 | * renamed BasicMinecraftParsers to MinecraftParsers 87 | * renamed/refactored EnrichmentClassses into BukkitEnrichment and ScalaEnrichment. 88 | * Changed the way commands work, they now take four arguments: name, description, args, body 89 | where before, args and body were merged together. 90 | * made Parser into a legit monad. 91 | * added filterWith to Parser, changed time parser to use it. 92 | 93 | ## Example/Other Code Changes 94 | 95 | * moved block changer to its own file and documented it. 96 | * changed BlockChanger parser to (material or noArguments), which has a 97 | little bit better error semantics than material.? 98 | * added Shock command example 99 | * added release script 100 | 101 | ## Documentation: 102 | 103 | * added scaladoc link to docs 104 | * added tiny shock command example (commands example) to main page 105 | * added bukkit.md, which is the main md for the plugin on bukkit. 106 | * documented parser combinator library, and cleaned it up a little. 107 | * added piles of other scaladoc to make sure it looks good in the generated html. 108 | * created http://joshcough.github.com/MinecraftPlugins/ 109 | * created http://joshcough.github.com/MinecraftPlugins/scaladoc 110 | -------------------------------------------------------------------------------- /examples/Arena/Arena.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.Material 4 | import Material._ 5 | import org.bukkit.event.player.PlayerMoveEvent 6 | import org.bukkit.block.Block 7 | import com.joshcough.minecraft.{Cube, CubeState, CommandsPlugin, ListenersPlugin} 8 | 9 | /** 10 | * The goal of this plugin was to have combatants enter an arena and 11 | * then fight to the death, but I never finished it. 12 | * 13 | * As of now, it does allow you to create arenas, and it notifies 14 | * players when they are entering and exiting an arena. This could 15 | * be a good start for a Trap plugin, or any other kind of thing 16 | * you might want to do when a player enters a particular area. 17 | */ 18 | class Arena extends ListenersPlugin with CommandsPlugin with CubeState { 19 | 20 | val listeners = List( 21 | // classic world-edit style corner setters 22 | OnLeftClickBlock((p, e) => if (p isHoldingA STONE_AXE) { 23 | setFirstPosition(p, e.loc) 24 | e.cancel 25 | }), 26 | OnRightClickBlock((p, e) => 27 | if (p isHoldingA STONE_AXE) setSecondPosition(p, e.loc)), 28 | 29 | /** 30 | * The fun motion detection is here, and this is where you might plug in something awesome. 31 | */ 32 | OnPlayerMove((p, e) => for((cp, cube) <- cubes) 33 | if (entering(cube, e)) p ! ("You are entering the battle arena of " + cp.name + "!") 34 | else if (leaving(cube, e)) p ! ("You are leaving the battle arena of " + cp.name + "!") 35 | ) 36 | ) 37 | 38 | val commands = List( 39 | Command(name = "axe", desc = "Get an Arena wand.")(_.loc.dropItem(STONE_AXE)), 40 | // this isnt really needed, but it makes things easier to see in the game 41 | // try doing it, and then boucing in and out of the arena. 42 | Command( 43 | name = "make-arena", 44 | desc = "Set the walls and floor to the given material type.", 45 | args = material)( 46 | body = { case (p, m) => 47 | val c = cube(p) 48 | for(b <- c.toStream) if(c.onWall(b.point) || c.onFloor(b.point)) 49 | b changeTo m else b changeTo AIR 50 | } 51 | ) 52 | ) 53 | 54 | def entering(c: Cube[Block], e: PlayerMoveEvent) = 55 | c.contains(e.getTo.point) and ! c.contains(e.getFrom.point) 56 | def leaving (c: Cube[Block], e: PlayerMoveEvent) = 57 | c.contains(e.getFrom.point) and ! c.contains(e.getTo.point) 58 | } 59 | -------------------------------------------------------------------------------- /examples/BanArrows/BanArrows.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.entity.EntityType.ARROW 4 | import com.joshcough.minecraft.Listeners 5 | import Listeners._ 6 | 7 | class BanArrows extends ListeningFor(OnPlayerDamageByEntity { (p, e) => 8 | if (e.getDamager isAn ARROW) p ban "struck by an arrow!" 9 | }) -------------------------------------------------------------------------------- /examples/BlockChanger/BlockChanger.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.{CommandPlugin, ListenerPlugin} 4 | import org.bukkit.entity.Player 5 | import org.bukkit.Material 6 | 7 | /** 8 | * A more in depth version of BlockChangerGold, that has a command 9 | * that allows the user to change the type of block they use when 10 | * they punch blocks, and also allows for block changing to be disabled. 11 | */ 12 | class BlockChanger extends ListenerPlugin with CommandPlugin { 13 | // a map that stores users material preference. 14 | val users = collection.mutable.Map[Player, Material]() 15 | val listener = OnLeftClickBlock{ (p, e) => 16 | /** 17 | * a block has been punched. if the user has a block preference, 18 | * change the punched block to that material. if they dont, do nothing. 19 | * (they either haven't enabled bc, or they have explicitly disabled it) 20 | */ 21 | for(m <- users get p){ e.block changeTo m; e.cancel } 22 | } 23 | 24 | /** 25 | * This code specifies that there is one command in the plugin, called bc. 26 | * Users can enter 27 | * /bc some-material 28 | * /bc 29 | * 30 | * The first updates their material preference so that if they bunch a block 31 | * the block changes to that material. 32 | * 33 | * The second turns block changer off, so that if they punch a block 34 | * it doesn't change. 35 | */ 36 | val command = Command( 37 | name = "bc", 38 | desc = "Specify which material to change blocks to, or just /bc to turn off", 39 | // this command works with either a material argument, or no arguments. 40 | args = material or nothing)( 41 | body = { 42 | // if they have typed in a material argument, save it in the map 43 | case (p, Left(m)) => users += (p -> m); p ! s"bc using: $m" 44 | // if they type no arguments, turn block changer off (remove them from the map). 45 | case (p, _) => users -= p; p ! "bc has been disabled" 46 | } 47 | ) 48 | } -------------------------------------------------------------------------------- /examples/BlockChangerGold/BlockChangerGold.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.Listeners._ 4 | import com.joshcough.minecraft.Listeners.ListeningFor 5 | import org.bukkit.Material 6 | import Material.GOLD_BLOCK 7 | 8 | /** 9 | * A plugin that changes blocks to gold whenever they are punched. 10 | */ 11 | class BlockChangerGold extends ListeningFor(OnLeftClickBlock{(p, e) => 12 | e.block changeTo GOLD_BLOCK; e.cancel 13 | }) 14 | -------------------------------------------------------------------------------- /examples/Farmer/Farmer.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.Material._ 4 | import org.bukkit.block.Block 5 | import org.bukkit.entity.Player 6 | import com.joshcough.minecraft.{ListenersPlugin, Listeners} 7 | import Listeners._ 8 | 9 | class Farmer extends ListenersPlugin { 10 | def dropSeedsAt(b: Block) = b.loc.dropItem(SEEDS) 11 | def hasHoe(p: Player) = p.isHoldingAnyOf(WOOD_HOE, STONE_HOE, IRON_HOE, GOLD_HOE, DIAMOND_HOE) 12 | val listeners = List( 13 | OnBlockBreak{ (b,_,_) => if (b is LONG_GRASS) dropSeedsAt(b) }, 14 | OnRightClickBlock{ (p, e) => if (hasHoe(p) and (e.block is GRASS)) dropSeedsAt(e.block) } 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /examples/GetOffMyLawn/GetOffMyLawn.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.event.player.PlayerMoveEvent 4 | import com.joshcough.minecraft.{Cube, CubeState, CommandsPlugin, ListenerPlugin} 5 | import org.bukkit.block.Block 6 | 7 | /** 8 | * Notifies you any time someone comes onto your lawn, 9 | * so that you can yell at them, kick them off, and shock them with lightning. 10 | * 11 | * Very similar in functionality to the Arena plugin in this same directory. 12 | */ 13 | class GetOffMyLawn extends ListenerPlugin with CommandsPlugin with CubeState { 14 | 15 | def movingOntoLawn(e:PlayerMoveEvent, lawn: Cube[Block]) = 16 | lawn.contains(e.getTo.point) && ! lawn.contains(e.getFrom.point) 17 | 18 | val listener = OnPlayerMove((p, e) => 19 | for ((owner, lawn) <- cubes; if p != owner && movingOntoLawn(e, lawn)) 20 | owner ! s"${p.name} is on your lawn!" 21 | ) 22 | 23 | val commands = List( 24 | Command("/pos1", "Set the first position") (p => setFirstPosition(p, p.loc)), 25 | Command("/pos2", "Set the second position")(p => setSecondPosition(p, p.loc)), 26 | Command( 27 | name = "GetOffMyLawn", 28 | desc = "Kicks everyone off your lawn, and shocks them with lightning")( 29 | body = owner => { 30 | val lawn = cube(owner) 31 | for(p <- lawn.players; if(p != owner)) { 32 | p.teleportTo(p.world.getHighestBlockAt(p.world(lawn.maxX + 5, 0, lawn.maxZ + 5).loc)) 33 | p shockWith s"'Get off my lawn!!!', said ${owner.name}." 34 | } 35 | } 36 | ) 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /examples/God/God.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.{CommandPlugin, ListenerPlugin} 4 | import org.bukkit.entity.Player 5 | 6 | // this same plugin exists in microexamples, and elsewhere, but that's ok. 7 | class God extends ListenerPlugin with CommandPlugin { 8 | val gods = collection.mutable.Map[Player, Boolean]().withDefaultValue(false) 9 | val listener = OnPlayerDamage { (p, e) => e cancelIf gods(p) } 10 | val command = Command( 11 | name = "god", desc = "Toggle God mode.")( 12 | body = p => { 13 | gods.update(p, ! gods(p)) 14 | p ! s"god mode ${if(gods(p)) "enabled" else "disabled"}" 15 | } 16 | ) 17 | } -------------------------------------------------------------------------------- /examples/LightningArrows/LightningArrows.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.Listeners._ 4 | import org.bukkit.entity.EntityType.ARROW 5 | 6 | class LightningArrows extends ListeningFor(OnEntityDamageByEntity { e => 7 | if (e.damager isAn ARROW) e.damagee.shock 8 | }) -------------------------------------------------------------------------------- /examples/MultiPlayerCommands/MultiPlayerCommands.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.GameMode._ 4 | import org.bukkit.Material._ 5 | import org.bukkit.entity.EntityType._ 6 | import scala.collection.JavaConversions._ 7 | import com.joshcough.minecraft.CommandsPlugin 8 | import org.bukkit.Material 9 | 10 | /** 11 | * Classic MultiPlayerCommands plugin, done in Scala. 12 | * Gives a whole pile of useful commands. 13 | * Their descriptions serve well as documentation. 14 | */ 15 | class MultiPlayerCommands extends CommandsPlugin { 16 | 17 | val commands = List( 18 | 19 | Command("goto", "Teleport!", player or location){ case (you, e) => 20 | e.fold(them => you teleportTo them, loc => you.teleport(loc of you.world)) 21 | }, 22 | 23 | Command("up", "Go up to the surface.")(_.surface), 24 | 25 | Command("set-time", "Sets the time.", time){ case (p, n) => p.world setTime n }, 26 | 27 | Command("day", "Sets the time to day (1000).") (_.world setTime 1000), 28 | 29 | Command("night", "Sets the time to night (15000).")(_.world setTime 15000), 30 | 31 | Command("gms", "Set your game mode to survival.")(_ setGameMode SURVIVAL), 32 | 33 | Command("gmc", "Set your game mode to creative.")(_ setGameMode CREATIVE), 34 | 35 | Command("gm", "Set your game mode.", gamemode){ case (p, gm) => p setGameMode gm }, 36 | 37 | Command("entities", "Display all the entities.")(p => p !* (p.world.entities.map(_.toString): _*)), 38 | 39 | Command("spawn", "Spawn some mobs.", entity ~ int.?.named("number to spawn")){ 40 | case (p, e ~ n) => p.loc.spawnN(e, n.fold(1)(id)) 41 | }, 42 | 43 | Command("ban", "Ban some players.", anyString.named("player").+){ case (you, them) => 44 | server.findOnlinePlayers (them) foreach { _ ban s"${you.name} doesn't like you." } 45 | server.findOfflinePlayers(them) foreach { _ setBanned true } 46 | }, 47 | 48 | Command("box", "Put a box around yourself, made of any material.", material){ case (p,m) => 49 | p.blocksAround.foreach(_ changeTo m) 50 | }, 51 | 52 | Command("safe", "Put yourself in a box made of bedrock.")( 53 | _.blocksAround.foreach(_ changeTo BEDROCK) 54 | ), 55 | 56 | OpOnly(P2P("punish", "Puts someone in a box made of bedrock.")((you, them) => 57 | you.doTo(them, them.blocksAround.foreach(_ changeTo BEDROCK), "punished") 58 | )), 59 | 60 | Command("drill", "Drill down to bedrock immediately.")(p => 61 | for (b <- p.blockOn.blocksBelow.takeWhile(_ isNot BEDROCK); if b isNot AIR) { 62 | b.erase 63 | if (b.blockBelow is BEDROCK) b nthBlockAbove 2 changeTo STATIONARY_WATER 64 | } 65 | ), 66 | 67 | Command("kill", "Kill entities.", ("player" ~ player) or entity){ 68 | case (killer, Left(_ ~ deadMan)) => killer kill deadMan 69 | case (killer, Right(e)) => killer.world.entities filter { _ isAn e } foreach (_.remove) 70 | }, 71 | 72 | OpOnly(P2P("creeper-kill", "Surround a player with creepers")((_, them) => { 73 | them setGameMode SURVIVAL 74 | them.loc.block.neighbors8 foreach (_.loc spawn CREEPER) 75 | })), 76 | 77 | OpOnly(P2P("feed", "Fill a players hunger bar.")((you, them) => 78 | you.doTo(them, them setFoodLevel 20, "fed")) 79 | ), 80 | 81 | OpOnly(P2P("starve", "Drain a players hunger bar.")((you, them) => 82 | you.doTo(them, them setFoodLevel 0, "fed")) 83 | ), 84 | 85 | OpOnly(P2P("shock", "Shock a player.")((you, them) => you.doTo(them, them.shock, "shocked"))), 86 | 87 | OpOnly(Command("lamp", "Spawns a lamp wherever you are looking."){ p => 88 | val b = p.getTargetBlock(null, 1000) 89 | if((b is Material.AIR) || math.abs(p.x - b.x) > 50 || math.abs(p.z - b.z) > 50) 90 | p ! s"That's more than 50 blocks away. (x: ${math.abs(p.x - b.x)}) (z: ${math.abs(p.z - b.z)})" 91 | else { 92 | b.blockBelow.changeTo(Material.REDSTONE_TORCH_ON) 93 | b.changeTo(Material.REDSTONE_LAMP_ON) 94 | } 95 | }), 96 | 97 | Command("jump", "Jump really high.", (int ~ int ~ int) or eof){ 98 | case (p, Left(x ~ y ~ z)) => p.setVelocity(new org.bukkit.util.Vector(x, y, z)) 99 | case (p, Right(_)) => p.setVelocity(new org.bukkit.util.Vector(0, 10, 0)) 100 | } 101 | ) 102 | } 103 | 104 | /** 105 | 106 | p = player 107 | t = target 108 | 109 | looking down x axis: 110 | p.x = 0, t.x = 10, p.z = 0, t.z = 0 111 | put all the weight in x: Vector(x=10, y=10, z=0) 112 | 113 | looking down z axis: 114 | p.z = 0, t.z = 10, p.x = 0, t.x = 0 115 | put all the weight in z: Vector(x=0, y=10, z=10) 116 | 117 | looking in between 118 | p.x = 0, p.z = 10, t.x = 5, t.z = 5 119 | put equal weight in both: Vector(x=10, y=10, z=10) 120 | 121 | looking more towards the x axis: 122 | p.x = 0, p.z = 10, t.x = 7.5, t.z = 2.5 123 | hmm...use those weights?: Vector(x=7.5, y=10, z=2.5) 124 | 125 | similar with z axis. 126 | 127 | */ -------------------------------------------------------------------------------- /examples/NoRain/NoRain.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.{ListenerPlugin, Listeners} 4 | import Listeners._ 5 | 6 | class NoRain extends ListenerPlugin { 7 | val listener = OnWeatherChange(e => e.cancelIf(e.rain, broadcast("Put up an umbrella."))) 8 | } -------------------------------------------------------------------------------- /examples/PluginCommander/PluginCommander.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.CommandsPlugin 4 | 5 | class PluginCommander extends CommandsPlugin { 6 | val commands = List( 7 | Command("enable", "Enable some plugins", plugin.+){ 8 | case (_, plugins) => plugins foreach pluginManager.enablePlugin 9 | }, 10 | Command("disable", "Disable some plugins", plugin.+){ 11 | case (_, plugins) => plugins foreach pluginManager.disablePlugin 12 | } 13 | ) 14 | } -------------------------------------------------------------------------------- /examples/Shock/Shock.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.CommandPlugin 4 | 5 | class Shock extends CommandPlugin { 6 | val command = Command("shock", "shock a player", player){ case (you, them) => 7 | them.shock 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/Thor/Thor.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.Listeners._ 4 | import com.joshcough.minecraft.Listeners.ListeningFor 5 | import org.bukkit.Material.DIAMOND_AXE 6 | 7 | class Thor extends ListeningFor(OnEntityDamageByPlayer { (damagee, thor, _) => 8 | if (thor isHoldingA DIAMOND_AXE) damagee.shock 9 | }) -------------------------------------------------------------------------------- /examples/TreeDelogger/TreeDelogger.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.Material._ 4 | import com.joshcough.minecraft.Listeners 5 | import Listeners._ 6 | 7 | class TreeDelogger extends ListeningFor(OnBlockBreak { (b,_,_) => 8 | if (b isA LOG) for (b <- b.andBlocksAbove.takeWhile(_ isA LOG)) b.erase 9 | }) 10 | -------------------------------------------------------------------------------- /examples/YellowBrickRoad/YellowBrickRoad.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.Material._ 4 | import com.joshcough.minecraft.Listeners 5 | import Listeners._ 6 | 7 | class YellowBrickRoad extends ListeningFor(OnPlayerMove((p, e) => 8 | if (p.blockOn isNot AIR) p.blockOn changeTo GOLD_BLOCK 9 | )) 10 | -------------------------------------------------------------------------------- /examples/ZombieApocalypse/ZombieApocalypse.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import org.bukkit.entity.EntityType.ZOMBIE 4 | import com.joshcough.minecraft.Listeners 5 | import Listeners._ 6 | 7 | class ZombieApocalypse extends ListeningFor(OnPlayerDeath { (p, _) => p.loc spawn ZOMBIE }) 8 | -------------------------------------------------------------------------------- /examples/src/main/scala/com/joshcough/minecraft/examples/NPCTest.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft.examples 2 | // 3 | //import com.joshcough.minecraft.{NPCPlugin, Command, CommandsPlugin} 4 | //import org.bukkit.entity.{EntityType, LivingEntity} 5 | // 6 | //class NPCTest extends CommandsPlugin with NPCPlugin { 7 | // 8 | // val npcs = collection.mutable.Map[String, LivingEntity]() 9 | // val ids = Iterator.from(0) 10 | // // this parses an npc by its name. 11 | // val npcTok = token("npc")(npcs.get) 12 | // 13 | // val commands = List( 14 | // Command("npc", "Spawn an NPC", args(anyString) { 15 | // case (p, s) => 16 | // npcs += (s -> human(s, p.loc)) 17 | // }), 18 | // Command("npc:spawn", "Spawn an Entity", args(entity){ case (p, e) => 19 | // val id = ids.next.toString 20 | // val npcEntity = e match { 21 | // case EntityType.WOLF => wolf(id, p.loc) 22 | // } 23 | // npcs += (id -> npcEntity) 24 | // }), 25 | // Command("npc:fd", "Move an NPC forward", args(npcTok ~ int) { 26 | // case (p, npc ~ distance) => 27 | // npc.teleport(p.world(npc.x + distance, npc.y, npc.z)) 28 | // }), 29 | // Command("npc:tp", "Teleport an NPC", args(npcTok ~ int ~ int) { 30 | // case (p, npc ~ x ~ z) => 31 | // npc.teleport(p.world(x, npc.y, z)) 32 | // }), 33 | // Command("ncp:kill-npc", "Kill an NPC", args(npcTok) { 34 | // case (p, npc) => 35 | // npcs -= npc.getEntityId.toString 36 | // npc.die 37 | // }) 38 | // ) 39 | //} 40 | -------------------------------------------------------------------------------- /examples/src/main/scala/com/joshcough/minecraft/examples/SimplePlugins.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft.examples 2 | // 3 | //import org.bukkit.ChatColor._ 4 | //import com.joshcough.minecraft.{CommandsPlugin, Listeners} 5 | //import Listeners._ 6 | // 7 | //object Curses { 8 | // import org.bukkit.event.player.PlayerChatEvent 9 | // val curses = List("btt", "tiju", "gvdl", "cjudi").map(_.map(c => (c - 1).toChar)) 10 | // def containsSwear(event: PlayerChatEvent) = 11 | // curses.filter(event.getMessage.toLowerCase.contains(_)).size > 0 12 | // def handle(e: PlayerChatEvent, f: => Unit) = e.cancelIf(containsSwear(e), f) 13 | //} 14 | // 15 | //class CurseBan extends ListeningFor(OnPlayerChat((p, e) => 16 | // Curses.handle(e, e.getPlayer.ban("no swearing"))) 17 | //) 18 | // 19 | //class CursePreventer extends ListeningFor(OnPlayerChat((p, e) => Curses.handle(e, ()))) 20 | // 21 | //class PermissionsTest extends CommandsPlugin { self => 22 | // val permission = anyStringAs("permission") 23 | // val commands = List( 24 | // Command("give-perm", "Give a player a permission", permission){ 25 | // case (p, perm) => p.addAttachment(self).setPermission(perm, true) 26 | // }, 27 | // Command("test-perm", "Test", permission){ 28 | // case (p, perm) => 29 | // if (p hasPermission perm) p ! s"you have permission: $perm" 30 | // else p ! RED(s"you don't have permission: $perm") 31 | // } 32 | // ) 33 | //} 34 | -------------------------------------------------------------------------------- /examples/src/main/scala/com/joshcough/minecraft/examples/WarpPlugin.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft.examples 2 | // 3 | //import com.joshcough.minecraft.DBPluginWithCommands 4 | //import org.bukkit.{Location, World} 5 | //import org.bukkit.entity.Player 6 | ////import org.squeryl.Schema 7 | ////import org.squeryl.PrimitiveTypeMode._ 8 | // 9 | ///** 10 | // * Classic Warp Plugin example, done in Scala. 11 | // * 12 | // * Allows players to create warp locations that they can 13 | // * later warp to. This is good for getting around quickly, 14 | // * and even better for not worrying about getting lost. 15 | // * 16 | // * This plugin also serves as a useful example of how to use the database. 17 | // * The Warp class is an 'Entity' that gets saved in the database. 18 | // * Any data saved in the database persists across user logouts, and server restarts. 19 | // */ 20 | //class WarpPlugin extends DBPluginWithCommands { 21 | // 22 | // lazy val db = new Schema { 23 | // private val warps = table[Warp] 24 | // // insert a warp 25 | // def insert(w: Warp) = warps.insert(w) 26 | // // find all the warps 27 | // def allWarps = from(warps)(select(_)).toList 28 | // // delete a warp 29 | // def delete(w: Warp) = warps.deleteWhere(_.id === w.id) 30 | // // find all the warps for the given player 31 | // def warpsFor(p: Player): List[Warp] = 32 | // from(warps)(w => where(w.player === p.name) select(w)).toList 33 | // // Tries to find a warp for the given player, and then runs a function on it 34 | // // (such as warping to it, or deleting it) 35 | // def withWarp(p: Player, warpName: String)(f: Warp => Unit) = 36 | // from(warps)(w => where(w.player === p.name and w.name === warpName) select(w)). 37 | // headOption.fold(p ! s"No such warp $warpName")(f) 38 | // } 39 | // 40 | // def warpToken = anyStringAs("warp-name") 41 | // 42 | // val commands = List( 43 | // DBCommand("warps", "List all warps.")(p => db.warpsFor(p).foreach(w => p ! w.toString)), 44 | // DBCommand("warp", "Warp to the given warp location.", warpToken){ case (p, wt) => 45 | // db.withWarp(p, wt)(w => p teleport w.location(p.world)) 46 | // }, 47 | // DBCommand("set-warp", "Create a new warp location.", warpToken){ case (p, warpName) => 48 | // db.insert(Warp(0, warpName, p.name, p.x, p.y, p.z)) 49 | // p ! s"created warp: $warpName" 50 | // }, 51 | // DBCommand("delete-warp", "Delete a warp location.", warpToken){ case (p, wt) => 52 | // db.withWarp(p, wt){w => db.delete(w); p ! s"deleted warp: ${w.name}" } 53 | // }, 54 | // DBCommand("delete-all", "Delete all your warps.")(p => 55 | // db.warpsFor(p).foreach{ w => p ! s"deleting: $w"; db.delete(w); } 56 | // ), 57 | // OpOnly(DBCommand("purge-warps-database", "Delete all warps in the database.")(p => 58 | // db.allWarps.foreach { w => p ! s"deleting: $w"; db.delete(w) }) 59 | // ) 60 | // ) 61 | //} 62 | // 63 | //case class Warp(id: Long, name: String, player: String, x: Double, y: Double, z: Double) { 64 | // def this() = this(0,"","",0,0,0) 65 | // def location(world: World) = new Location(world, x, y, z) 66 | // override def toString = player + "." + name + (x, y, z) 67 | //} 68 | -------------------------------------------------------------------------------- /examples/src/main/scala/com/joshcough/minecraft/examples/WorldEditDemo.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft.examples 2 | 3 | import com.joshcough.minecraft.{CommandsPlugin, ListenersPlugin} 4 | import org.bukkit.{Location, Material} 5 | import org.bukkit.block.Block 6 | import org.bukkit.entity.Player 7 | import Material.WOOD_AXE 8 | 9 | class WorldEditDemo extends ListenersPlugin with CommandsPlugin { 10 | 11 | val corners = collection.mutable.Map[Player, List[Location]]().withDefaultValue(Nil) 12 | 13 | val listeners = List( 14 | OnLeftClickBlock ((p, e) => if (p isHoldingA WOOD_AXE) { setFirstPos (p, e.loc); e.cancel }), 15 | OnRightClickBlock((p, e) => if (p isHoldingA WOOD_AXE) { setSecondPos(p, e.loc) }) 16 | ) 17 | 18 | val commands = List( 19 | Command( 20 | name = "set" , 21 | desc = "Set all the selected blocks to the given material type.", 22 | args = material)( 23 | body = { case (p, m) => for(b <- cube(p)) b changeTo m } 24 | ), 25 | Command( 26 | name = "replace", 27 | desc = "Replace all the selected blocks of the first material type to the second material type.", 28 | args = material ~ material)( 29 | body = { case (p, oldM ~ newM) => for(b <- cube(p); if(b is oldM)) b changeTo newM } 30 | ) 31 | ) 32 | 33 | def cube(p:Player):Stream[Block] = corners(p).filter(_.length == 2) match { 34 | case List(loc1, loc2) => loc1.cubeTo(loc2).blocks 35 | case _ => p ! "Both corners must be set!"; Stream[Block]() 36 | } 37 | 38 | def setFirstPos(p:Player,loc: Location): Unit = { 39 | corners += (p -> List(loc)) 40 | p ! s"first corner set to: ${loc.xyz}" 41 | } 42 | 43 | def setSecondPos(p:Player,loc2: Location): Unit = corners(p) match { 44 | case loc1 :: _ => 45 | corners += (p -> List(loc1, loc2)) 46 | p ! s"second corner set to: ${loc2.xyz}" 47 | case Nil => 48 | p ! "set corner one first! (with a left click)" 49 | } 50 | } -------------------------------------------------------------------------------- /examples/src/test/scala/com/joshcough/minecraft/examples/CopyDataTests.scala: -------------------------------------------------------------------------------- 1 | //package com.joshcough.minecraft.examples 2 | // 3 | //import org.scalacheck._ 4 | //import org.scalacheck.Prop._ 5 | //import Gen._ 6 | //import Arbitrary.arbitrary 7 | //import com.joshcough.minecraft.{Cube, Point} 8 | //import java.io.{DataOutputStream, DataInputStream, ByteArrayInputStream, ByteArrayOutputStream} 9 | // 10 | //object CopyDataTests extends Properties("CopyDataTests") { 11 | // type C = Cube[Point] 12 | // val smallInteger = Gen.choose(-2,2) 13 | // val genSmallCube: Gen[C] = for { 14 | // x1 <- smallInteger; y1 <- smallInteger; z1 <- smallInteger 15 | // x2 <- smallInteger; y2 <- smallInteger; z2 <- smallInteger 16 | // } yield Cube(Point(x1, y1, z1), Point(x2, y2, z2))(identity) 17 | // implicit val smallCubes = Arbitrary(genSmallCube) 18 | // def toBlockData(p:Point): BlockData = 19 | // BlockData(p.x, p.y, p.z, (math.random * 1024).toInt, (math.random * 256).toByte) 20 | // def toCopyData(c: Cube[Point]): CopyData = 21 | // CopyData(c.corner1, c.corner2, c.size.toLong, c.toStream.map(toBlockData)) 22 | // implicit val copyDatas = Arbitrary(genSmallCube.map(toCopyData)) 23 | // def test(name:String)(f: => Prop) = property(name) = trying(f) 24 | // def trying(f: => Prop) = secure { 25 | // try f catch { case e: Throwable => e.printStackTrace; throw e } 26 | // } 27 | // def roundTrip(cd: CopyData): CopyData = { 28 | // val bos = new ByteArrayOutputStream() 29 | // val os = new DataOutputStream(bos) 30 | // CopyData.write(cd, os) 31 | // os.flush() 32 | // CopyData.read(new DataInputStream(new ByteArrayInputStream(bos.toByteArray))) 33 | // } 34 | // 35 | // test("paste then mirror y same as mirror y then paste")(forAll{ (cd:CopyData) => 36 | // cd ?= roundTrip(cd) 37 | // }) 38 | //} 39 | -------------------------------------------------------------------------------- /microexample/BlockChanger.scala: -------------------------------------------------------------------------------- 1 | package com.example 2 | 3 | import com.joshcough.minecraft.{CommandPlugin, ListenerPlugin} 4 | import org.bukkit.entity.Player 5 | import org.bukkit.Material 6 | 7 | class BlockChanger extends ListenerPlugin with CommandPlugin { 8 | val users = collection.mutable.Map[Player, Material]() 9 | val listener = OnLeftClickBlock{ (p, e) => 10 | for(m <- users.get(p)) { e.block changeTo m; e.cancel } 11 | } 12 | val command = Command( 13 | name = "bc", 14 | desc = "Specify which material to change blocks to, or just /bc to turn off", 15 | args = material or eof)( 16 | body = { 17 | case (p, Left(m)) => users += (p -> m); p ! s"bc using: $m" 18 | case (p, _) => users -= p; p ! "bc has been disabled" 19 | } 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /microexample/Makefile: -------------------------------------------------------------------------------- 1 | go: setup run-server 2 | 3 | compile: 4 | ./sbt package 5 | mkdir -p bukkit/plugins 6 | cp target/scala-2.11/block-changer-plugin_2.11-0.3.4.jar bukkit/plugins 7 | 8 | run-server: 9 | cd bukkit && ./run-server.sh 10 | 11 | setup: compile 12 | cp ~/.ivy2/cache/com.joshcough/scala-minecraft-plugin-api_2.11/jars/scala-minecraft-plugin-api_2.11-0.3.4.jar bukkit/plugins/ 13 | curl -L "http://dl.bintray.com/joshcough/maven/com/joshcough/scala-minecraft-scala-library_2.11/0.3.4/scala-minecraft-scala-library_2.11-0.3.4-assembly.jar" -o bukkit/plugins/scala-minecraft-scala-library_2.11-0.3.4-assembly.jar 14 | 15 | clean: 16 | rm -rf target 17 | rm -rf bukkit/plugins/* 18 | -------------------------------------------------------------------------------- /microexample/README.md: -------------------------------------------------------------------------------- 1 | MinecraftPluginsScalaExample 2 | ============================ 3 | 4 | A simple example of how to use my Scala API for creating minecraft plugins. To run it: 5 | 6 | ## 1a / Prerequisites 7 | 8 | These instructions assume you are on a mac, and have git installed on your machine. 9 | They probably aren't that difficult to translate into Windows or whatever. 10 | 11 | * Git can be downloaded here: http://git-scm.com/downloads 12 | * Macs can be purchased here: http://www.apple.com 13 | 14 | ## 1. Clone the example repo 15 | 16 | The example repo is here: https://github.com/joshcough/MinecraftPluginsScalaExample. To clone it, run: 17 | 18 | `git clone git://github.com/joshcough/MinecraftPluginsScalaExample.git` 19 | 20 | The example repo contains one simple plugin: BlockChanger, and the code and documentation for it can be found [here](https://github.com/joshcough/MinecraftPluginsScalaExample/blob/master/src/main/scala/BlockChanger.scala). It demonstrates how to write Listeners and Commands. 21 | 22 | The example repo also contains a bukkit server, and a Makefile which will automatically build everything, and run the server for you. 23 | 24 | ## 2. make and play 25 | 26 | Run `make` at the command line, fire up Minecraft, connect to your local server, and have fun! 27 | 28 | ## 3. Playing on your own server 29 | 30 | If you want to play on your own server, after running `make`, copy the jars in bukkit/plugins to your bukkit plugins folder. Start your server, and play. 31 | 32 | ## More Information 33 | 34 | More information on writing plugins in Scala can be found on the [MinecraftPlugins/wiki](https://github.com/joshcough/MinecraftPlugins/wiki) 35 | -------------------------------------------------------------------------------- /microexample/build.sbt: -------------------------------------------------------------------------------- 1 | name := "Block Changer Plugin" 2 | 3 | version := "0.3.4" 4 | 5 | organization := "examples" 6 | 7 | scalaVersion := "2.11.6" 8 | 9 | // this specifies where to get the bukkit jar from. 10 | resolvers += "Bukkit" at "http://repo.bukkit.org/content/groups/public/" 11 | 12 | resolvers += Resolver.jcenterRepo 13 | 14 | seq(pluginYmlSettings("com.example.BlockChanger", "JoshCough"):_*) 15 | 16 | // plugins need to depend on com.joshcough.minecraft, and bukkit 17 | libraryDependencies ++= Seq( 18 | "com.joshcough" %% "scala-minecraft-plugin-api" % "0.3.4", 19 | "org.bukkit" % "bukkit" % "1.7.2-R0.2" 20 | ) 21 | -------------------------------------------------------------------------------- /microexample/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /microexample/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 2 | 3 | resolvers ++= Seq("Bukkit" at "http://repo.bukkit.org/content/repositories/releases") 4 | 5 | resolvers += Resolver.jcenterRepo 6 | 7 | resolvers += Resolver.url( 8 | "bintray-sbt-plugin-releases", 9 | url("http://dl.bintray.com/content/sbt/sbt-plugin-releases"))( 10 | Resolver.ivyStylePatterns) 11 | 12 | addSbtPlugin("com.joshcough" % "minecraft-sbt-plugin" % "0.3.5") -------------------------------------------------------------------------------- /microexample/project/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 2 | -------------------------------------------------------------------------------- /other/clojure/src/main/clojure/com/joshcough/minecraft/testplugin.clj: -------------------------------------------------------------------------------- 1 | (ns com.joshcough.minecraft.test.testplugin 2 | (:gen-class :extends org.bukkit.plugin.java.JavaPlugin)) 3 | 4 | (defn -toString [this] "hi") 5 | 6 | (defn -onEnable [this] 7 | (print "test clojure plugin enabled!") 8 | ) 9 | 10 | (defn -main [greetee] (println (str "Hello " greetee "!"))) 11 | -------------------------------------------------------------------------------- /other/clojure/src/test/clojure/com/joshcough/minecraft/test/testplugin_test.clj: -------------------------------------------------------------------------------- 1 | (ns com.joshcough.minecraft.test.testplugin_test 2 | (:use clojure.test 3 | com.joshcough.minecraft.test.testplugin)) 4 | 5 | (deftest a-test (testing "I'm fixed." (is (= 0 0)))) 6 | 7 | (deftest t1 8 | (testing "create something" 9 | (is (= (.toString (new com.joshcough.minecraft.test.testplugin)) "hi")) 10 | ) 11 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/resources/clojureinscala/bool.mc: -------------------------------------------------------------------------------- 1 | ( 2 | (def and [a b] (if a b false)) 3 | (def or [a b] (if a true b)) 4 | (def zero? [x] (if (eq? x 0) true false)) 5 | (def not [x] (if x 1 0)) 6 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/resources/clojureinscala/list.mc: -------------------------------------------------------------------------------- 1 | ( 2 | ; http://stackoverflow.com/questions/6578615/how-to-use-scala-collection-immutable-list-in-a-java-code 3 | (val empty scala.collection.immutable.Nil$/MODULE$) 4 | (def cons [h t] (.apply scala.collection.immutable.$colon$colon$/MODULE$ h t)) 5 | 6 | ; all these should stay the same even if i change the underlying representation above 7 | ; but if i had pattern matching they could all be much nicer 8 | (def empty? [l] (or (eq? l empty) (eq? l false))) 9 | (def head [l] (.head l)) 10 | (def tail [l] (.tail l)) 11 | (def list? [l] (isa? l scala.collection.immutable.List)) 12 | 13 | (defrec append [l1 l2] (if (empty? l1) l2 (cons (head l1) (append (tail l1) l2)))) 14 | (val join append) 15 | (def unit [a] (cons a empty)) 16 | ;(a -> b -> a) -> a -> [b] -> a 17 | (defrec foldl [f init l] (if (empty? l) init (foldl f (f init (head l)) (tail l)))) 18 | ;(a -> b -> b) -> b -> [a] -> b 19 | (defrec foldr [f init l] (if (empty? l) init (f (head l) (foldr f init (tail l))))) 20 | (def bind [f xs] (foldr (fn [a b] (join (f a) b)) empty xs)) 21 | (defrec fmap [f xs] (bind (fn [x] (unit (f x))) xs)) 22 | (defrec filter [p xs] (foldr (fn [a b] (if (p a) (cons a b) b)) empty xs)) 23 | ) 24 | -------------------------------------------------------------------------------- /other/minelang/src/main/resources/clojureinscala/math.mc: -------------------------------------------------------------------------------- 1 | ( 2 | (def even [n] (eq (% n 2) 0)) 3 | (def random-int [min max] (+ min (* (random) (- max min)))) 4 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/resources/minelang/expand.mc: -------------------------------------------------------------------------------- 1 | (; simple, fun floor expansion program 2 | (let (c (cube:mk (XYZ) (loc (+ (X) 10) (Y) (+ (Z) 10)))) 3 | (begin 4 | (cube:set-walls c "stone") 5 | (cube:set-walls (.expandXZ c 1) "brick") 6 | (cube:set-walls (.expandXZ (.expandXZ c 1) 1) "gold_block") 7 | 8 | ; a different way to do the same thing above 9 | ; not quite as nice, but more functional 10 | (cube:set-walls 11 | (.expandXZ 12 | (cube:set-walls (.expandXZ (cube:set-walls c "stone") 1) "brick") 13 | 1 14 | ) 15 | "gold_block" 16 | ) 17 | ) 18 | ) 19 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/resources/minelang/factorial.mc: -------------------------------------------------------------------------------- 1 | ( 2 | (def test [] 3 | (letrec (fact (fn [n] (if (eq? n 0) 1 (* n (fact (- n 1)))))) 4 | (fact 5) 5 | ) 6 | ) 7 | 8 | (defrec factorial [n] (if (eq? n 0) 1 (* n (factorial (- n 1))))) 9 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/resources/minelang/house.mc: -------------------------------------------------------------------------------- 1 | (; program to build houses and cities 2 | 3 | ; see http://www.minecraftwiki.net/wiki/Data_values#Block_IDs for the block names 4 | (val house-blocks (list 1 4 5 7 14 15 16 17 20 21 22 24 35 41 42 43 45 47 5 | 48 49 56 57 73 74 87 89 98 112 121 23 124 125 129 133)) 6 | (val nr-house-blocks (.size house-blocks)) 7 | (def random-house-block [] (material (.apply house-blocks (random-int 0 nr-house-blocks)))) 8 | 9 | ; build a pyramid! 10 | ; Cube -> Material -> Cube 11 | (defrec pyramid [c m] 12 | (cube:set-walls c m) 13 | (let (closed (fn [c] (or (<= (- (.maxX c) (.minX c)) 1) (<= (- (.maxZ c) (.minZ c)) 1)))) 14 | (unless (closed c) (pyramid (.shiftY (.shrinkXZ c 1) 1) m)) 15 | ) 16 | ) 17 | 18 | ; build a single house 19 | ; Location -> Int -> Int -> Int -> Material -> Material -> Material -> Cube 20 | (def build-house [start-point h w d floor-m walls-m roof-m] 21 | (let* ((c (.growUp (.expandZ (.expandX (cube:mk start-point start-point) w) d) h)) 22 | (build-ceiling (fn (c m) (pyramid (.expandXZ (.ceiling c) 1) m) c))) 23 | (begin 24 | (build-ceiling (cube:set-walls (cube:set-floor (cube:set-all c "air") floor-m) walls-m) roof-m) 25 | c 26 | ) 27 | ) 28 | ) 29 | 30 | ; build a row of houses 31 | ; Location -> Int -> (Location -> Cube) -> Cube 32 | (defrec build-house-row [at nr-houses house-builder-f] 33 | (unless (eq? nr-houses 0) 34 | (let (c (house-builder-f at)) 35 | ; TODO: 20 isnt right here. the houses could be bigger than 20 wide... 36 | (build-house-row 37 | (loc 38 | (+ (* 2 (.width c)) (.getX at)) 39 | (.getY at) 40 | (.getZ at) 41 | ) 42 | (- nr-houses 1) 43 | house-builder-f 44 | ) 45 | ) 46 | ) 47 | ) 48 | 49 | (def random-building [center-point 50 | min-h max-h 51 | min-w max-w 52 | min-d max-d] 53 | (build-house 54 | center-point 55 | (random-int min-h max-h) 56 | (random-int min-w max-w) 57 | (random-int max-w max-d) 58 | (random-house-block) 59 | (random-house-block) 60 | (random-house-block) 61 | ) 62 | ) 63 | 64 | ; builds a skyscraper 65 | ; Location -> Cube 66 | (def build-skyscraper [l] (build-house l 50 8 10 "stone" "obsidian" "diamond_block")) 67 | (def build-random-skyscraper [l] (random-building l 68 | 20 100 ; min-h max-h 69 | 4 10 ; min-w max-w 70 | 4 10 ; min-d max-d 71 | )) 72 | 73 | ; builds a house - {5 -> wood plank, 17 -> wood, 20 -> glass} 74 | ; Location -> Cube 75 | (def build-normal-house [l] (build-house l 4 3 3 "5" "17" "20")) 76 | 77 | ; builds a row of skyscrapers. not really a full city, yet. 78 | ; -> Cube 79 | (def city [] (build-house-row (XYZ) 10 build-random-skyscraper)) 80 | 81 | ; builds a row of little houses. could be considered a village. 82 | ; -> Cube 83 | (def village [] (build-house-row (XYZ) 6 build-normal-house)) 84 | 85 | ; build a house, and then change the wall its walls every second for 100 seconds. 86 | (def living-house [] 87 | (let (c (normal-house (XYZ))) 88 | (spawn 100 1 (fn (n) (cube:set-walls c (if (even n) "gold_block" "gold_ore")))) 89 | ) 90 | ) 91 | ) -------------------------------------------------------------------------------- /other/minelang/src/main/scala/com/joshcough/minecraft/MineLang.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.bukkit.{Location, Material} 4 | import org.bukkit.entity.Player 5 | import ClojureInScala._ 6 | import AST._ 7 | import Parser._ 8 | import Interpreter._ 9 | import org.bukkit.block.Block 10 | 11 | object MineLangRepl { 12 | def main(args:Array[String]): Unit = new Repl(MineLang.mineLangSession(TestServer.player)).run 13 | } 14 | 15 | object MineLang { 16 | 17 | def mineLangSession(p:Player) = Session.withStdLib(new WorldEditExtension(p).lib) 18 | def run (code:String, p:Player): Any = runProgram(parse(code), p) 19 | def runExpr (code:String, p:Player): Any = runProgram(parse(s"($code)"), p) 20 | def runProgram(prog:Program, p:Player): Any = unbox(mineLangSession(p).runProgram(prog)) 21 | 22 | case class WorldEditExtension(p:Player) extends BukkitEnrichment { 23 | def evalToLocation(e:Expr, env:Env): Location = 24 | evalTo(e,env,"location"){ case ObjectValue(l:Location) => l } 25 | def evalToMaterial(e:Expr, env:Env): Material = 26 | evalTo(e,env,"material"){ 27 | case ObjectValue(m:Material) => m 28 | case ObjectValue(s:String) => MinecraftParsers.material(s).fold(sys.error)((m, _) => m) 29 | } 30 | def evalToCube(e:Expr, env:Env): Cube[Block] = 31 | evalTo(e,env,"cube"){ case ObjectValue(c:Cube[Block]) => c } 32 | 33 | val getMaterial = builtIn('material, (exps, env) => { 34 | eval(exps(0),env) match { 35 | case ObjectValue(s:String) => ObjectValue( 36 | MinecraftParsers.material(s).fold(sys.error)((m, _) => m) 37 | ) 38 | case ObjectValue(i:Int) => ObjectValue( 39 | MinecraftParsers.material(i.toString).fold(sys.error)((m, _) => m) 40 | ) 41 | case ev => sys error s"not a material: $ev" 42 | } 43 | }) 44 | val goto = builtInNil('goto, (exps, env) => { 45 | val loc = evalToLocation(exps(0),env) 46 | //p ! s"teleported to: ${loc.xyz}"; p.teleport(loc) 47 | }) 48 | val loc = builtIn('loc, (exps, env) => { 49 | val (xe,ye,ze) = (evalAndUnbox(exps(0),env),evalAndUnbox(exps(1),env),evalAndUnbox(exps(2),env)) 50 | if (allNumbers(List(xe,ye,ze))) 51 | ObjectValue(new Location(p.world,toInt(xe),toInt(ye),toInt(ze))) 52 | else sys error s"bad location data: ${(xe,ye,ze)}" 53 | }) 54 | // here are all the cube block mutation functions. 55 | def builtInCube(name:Symbol, eval: (List[Expr], Env) => Cube[Block]) = 56 | name -> BuiltinFunction(name, (es, env) => { ObjectValue(eval(es,env)) }) 57 | val setAll = builtInCube(Symbol("cube:set-all"), (exps, env) => { 58 | val c = evalToCube(exps(0), env) 59 | val m = evalToMaterial(exps(1), env) 60 | for(b <- c.toStream) b changeTo m 61 | //p ! s"setting all in $c to $m" 62 | c 63 | }) 64 | val mkCube = builtInCube(Symbol("cube:mk"), (exps, env) => { 65 | val c1 = evalToLocation(exps(0),env) 66 | val c2 = evalToLocation(exps(1),env) 67 | new Cube(Point(c1.xyz), Point(c2.xyz))(pt => p.world.getBlockAt(pt.x, pt.y, pt.z)) 68 | }) 69 | val changeSome = builtInCube(Symbol("cube:change"), (exps, env) => { 70 | val c = evalToCube(exps(0), env) 71 | val oldM = evalToMaterial(exps(1),env) 72 | val newM = evalToMaterial(exps(2),env) 73 | for(b <- c.toStream; if b is oldM) b changeTo newM 74 | //p ! s"changed $oldM in $c to $newM" 75 | c 76 | }) 77 | val setWalls = builtInCube(Symbol("cube:set-walls"), (exps, env) => { 78 | val c = evalToCube(exps(0), env) 79 | val m = evalToMaterial(exps(1),env) 80 | c.walls.foreach(_ changeTo m) 81 | //p ! s"set walls in $c to: $m" 82 | c 83 | }) 84 | val setFloor = builtInCube(Symbol("cube:set-floor"), (exps, env) => { 85 | val c = evalToCube(exps(0), env) 86 | val m = evalToMaterial(exps(1),env) 87 | c.floor.toStream.foreach(_ changeTo m) 88 | //p ! s"set floor in $c to: $m" 89 | c 90 | }) 91 | val message = builtInNil('message, (exps, env) => 92 | p ! exps.map(e => evalAndUnbox(e, env).toString).mkString("\n") 93 | ) 94 | 95 | val lib: Env = Map( 96 | loc, goto, 97 | 'MAXY -> ObjectValue(255), 98 | 'MINY -> ObjectValue(0), 99 | builtInNoArg('X, ObjectValue(p.x)), 100 | builtInNoArg('Y, ObjectValue(p.blockOn.y)), 101 | builtInNoArg('Z, ObjectValue(p.z)), 102 | builtInNoArg('XYZ, ObjectValue(p.blockOn.loc)), 103 | builtInNoArg('origin, ObjectValue(p.world.getHighestBlockAt(0,0))), 104 | // material functions 105 | getMaterial, 106 | // mutable world edit functions 107 | mkCube, setAll, changeSome, setWalls, setFloor, 108 | // send a message to the player 109 | message 110 | ) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /other/minelang/src/main/scala/com/joshcough/minecraft/MineLangPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import com.joshcough.minecraft.ClojureInScala._ 4 | import Reader._ 5 | import AST._ 6 | import Parser._ 7 | import com.joshcough.minecraft.MineLang._ 8 | import java.io.File 9 | 10 | class MineLangPlugin extends CommandsPlugin { 11 | 12 | // todo: we should be able to load more than one file 13 | // and this directory might be wrong now. 14 | lazy val houseDefs = new File("other/minelang/src/main/resources/minelang/house.mc") 15 | var defs: List[Def] = parseDefs(read(houseDefs)) 16 | 17 | val commands = List( 18 | Command("import", "import some defs", existingFile){ case (_, codeFile) => 19 | defs = defs ::: parseDefs(read(codeFile)) 20 | }, 21 | Command("run", "run a program", slurp){ case (p, code) => 22 | runProgram(Program(defs, parseExpr(read(code))), p) 23 | }, 24 | Command("reload-code", "reload some code")(p => defs = parseDefs(read(houseDefs))) 25 | ) 26 | } -------------------------------------------------------------------------------- /other/minelang/src/test/scala/com/joshcough/minecraft/ClojureInScalaTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck._ 4 | import org.scalacheck.Prop._ 5 | import ClojureInScala._ 6 | 7 | case class Point2D(x:Int, y:Int){ 8 | def this(x:java.lang.Integer, y:java.lang.Integer, z:Unit) = this(x, y) 9 | override def toString = s"($x,$y)" 10 | def invoke1(i:java.lang.Integer) = "6" 11 | def invoke2(i:Int) = "6" 12 | def invoke3(i:Int, i2:java.lang.Integer) = "6" 13 | def invoke4(i:Int, i2:java.lang.Integer*) = "6" 14 | def invokeFun1a(f: Any => Int) = f(7) 15 | def invokeFun1b(f: Int => Int) = f(8) 16 | def invokeFun2a(f: (Int, Int) => Int) = f(8, 9) 17 | } 18 | 19 | object ClojureInScalaTests extends Properties("ClojureInScalaTests") with BukkitEnrichment with TestHelpers { 20 | 21 | evalTest("constructorCall1", "(new com.joshcough.minecraft.Point2D 5 6)", Point2D(5,6)) 22 | evalTest("constructorCall2", "(new com.joshcough.minecraft.Point2D 5 6 nil)", Point2D(5,6)) 23 | evalTest("instanceCall0", "(.toString (new com.joshcough.minecraft.Point2D 5 6))", "(5,6)") 24 | evalTest("instanceCall1", "(.invoke1 (new com.joshcough.minecraft.Point2D 5 6) 0)", "6") 25 | evalTest("instanceCall2", "(.invoke2 (new com.joshcough.minecraft.Point2D 5 6) 0)", "6") 26 | evalTest("instanceCall3", "(.invoke3 (new com.joshcough.minecraft.Point2D 5 6) 0 0)", "6") 27 | 28 | // TODO: this passes in java 6, but fails in 7! 29 | //evalTest("staticCall1", "(java.lang.String/valueOf 5)", "5") 30 | 31 | evalTest("staticField1", "java.lang.Math/PI", Math.PI) 32 | evalTest("lamTest", "((fn (x) x) 7)", 7) 33 | evalTest("invokeWithFun1a", "(.invokeFun1a (new com.joshcough.minecraft.Point2D 5 6) (fn (x) x))", 7) 34 | evalTest("invokeWithFun1b", "(.invokeFun1b (new com.joshcough.minecraft.Point2D 5 6) (fn (x) (+ x x)))", 16) 35 | evalTest("invokeWithFun2a", "(.invokeFun2a (new com.joshcough.minecraft.Point2D 5 6) (fn (x y) (* 9 8)))", 72) 36 | evalTest("let1 test", "(let (a 5) a)", 5) 37 | evalTest("let2 test", "(let (a 5) (+ a a))", 10) 38 | evalTest("letNested test", "(let (a 5) (let (a 10) 10))", 10) 39 | evalTest("let* test 1", "(let* ((a 5) (b 6)) (+ a b))", 11) 40 | evalTest("let* test 2", "(let* ((a 5) (b (+ a 2))) (+ a b))", 12) 41 | evalTest("isa? test 1", """(isa? "hi" java.lang.String)""", true) 42 | evalTest("access empty", s"empty", Nil) 43 | evalTest("apply cons", s"(cons 1 empty)", List(1)) 44 | 45 | // TODO: this runs out of heap space! holy crap! 46 | //evalTest("list map" , "(map (fn (x) (* x x)) (cons 1 (cons 2 (cons 3 empty))))", List(1, 4, 9)) 47 | 48 | def evalTest(name:String, code:String, expected:Any) = test(name){ 49 | val actual = Session.withStdLib().runExpr(code)._1._2 50 | println(s"Result: $actual") 51 | actual ?= expected 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /other/minelang/src/test/scala/com/joshcough/minecraft/MineLangTests.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck._ 4 | import org.scalacheck.Prop._ 5 | import ClojureInScala._ 6 | import Reader._ 7 | import AST._ 8 | import Parser._ 9 | import MineLang._ 10 | import java.io.File 11 | 12 | object MineLangTests extends Properties("MineLangTests") with BukkitEnrichment with TestHelpers { 13 | 14 | val mineLangDir = new File("other/minelang/src/main/resources/minelang") 15 | val expandMc = mineLangDir.child("expand.mc") 16 | parseDefsTest("expand defs parse", expandMc, 0) 17 | evalTest("expand", expandMc.slurp, TestServer.world(12,3,12).cubeTo(TestServer.world(-2,3,-2))) 18 | 19 | val factorialDefs = mineLangDir.child("factorial.mc") 20 | parseDefsTest("factorial defs parse", factorialDefs, 2) 21 | evalWithDefsTest("factorial defs eval", "(test)", 120, factorialDefs) 22 | 23 | val houseDefs = mineLangDir.child("house.mc") 24 | parseDefsTest("house defs parse", houseDefs, 13) 25 | evalWithDefsTest("house defs eval", "(city)", (), houseDefs) 26 | 27 | def evalTest(name:String, code:String, expected:Any) = test(name){ run(code, expected) } 28 | 29 | def evalWithDefsTest(name:String, code:String, expected:Any, defs:File) = 30 | test(name) { runWithDefs(code, expected, parseDefs(read(defs))) } 31 | 32 | def parseDefsTest(name:String, code:File, expected:Int) = 33 | test(name) { 34 | val res = parseDefs(read(code)) 35 | //println(s"Parse Result:") 36 | //res.foreach(println) 37 | res.size ?= expected 38 | } 39 | 40 | def run(code:String, expected:Any) = { 41 | val actual = MineLang.run(code, TestServer.player) 42 | println(s"Result: $actual") 43 | actual ?= expected 44 | } 45 | 46 | def runWithDefs(code:String, expected:Any, defs:List[Def]) = { 47 | val actual = runProgram(Program(defs, parseExpr(read(code))), TestServer.player) 48 | println(s"Result: $actual") 49 | actual ?= expected 50 | } 51 | } 52 | 53 | // val testScriptFull = 54 | // """ 55 | // ((begin 56 | // (goto origin) 57 | // (corners (loc (+ X 20) (+ Y 50) (+ Z 20)) (loc (- X 20) Y (- Z 20))) 58 | // (floor stone) 59 | // (walls brick) 60 | // 7 61 | // )) 62 | // """.stripMargin.trim 63 | // 64 | // val valTest = "((val x 7) x)" 65 | // 66 | // val defTest = """ 67 | // ( 68 | // (def d (a1 a2) (begin (corners (loc a1 a1 a1) (loc a2 a2 a2)) (set stone) (+ a1 a2))) 69 | // (val x 7) 70 | // (let (g 9) (d g 7)) 71 | // ) 72 | // """ 73 | // property("origin") = secure { parseExpr("origin") } 74 | // property("XYZ") = secure { parseExpr("XYZ") } 75 | // property("(5 6 7)") = secure { parseExpr("(loc 5 6 7)") } 76 | // property("(5 Y 7)") = secure { parseExpr("(loc 5 Y 7)") } 77 | // property("((+ 5 10) Y 7)") = secure { parseExpr("(loc (+ 5 10) Y 7)") } 78 | // property("((+ X 20) Y (+ Z 20))") = secure { parseExpr("(loc (+ X 20) Y (+ Z 20))") } 79 | // property("((- X 20) Y (- Z 20))") = secure { parseExpr("(loc (- X 20) Y (- Z 20))") } 80 | // property("((- 5 10) (+ Y 20) Z)") = secure { parseExpr("(loc (- 5 10) (+ Y 20) Z)") } 81 | // property("origin") = secure { parseExpr("(set stone)") } 82 | // property("((goto origin))") = secure { run("((goto origin))") } 83 | // property("(corners XYZ origin)") = secure { run("((corners XYZ origin))") } 84 | // property("(corners XYZ (5 6 7))") = secure { run("((corners XYZ (loc 5 6 7)))") } 85 | // property("(corners (loc (+ X 20) Y (+ Z 20)) (loc (+ X 20) Y (+ Z 20)))") = 86 | // secure { run("((corners (loc (+ X 20) Y (+ Z 20)) (loc (+ X 20) Y (+ Z 20))))") } 87 | // property("(set stone)") = secure { run("((set stone))") } 88 | // property("(walls brick)") = secure { run("((walls brick))") } 89 | // property("testScriptFull") = secure { parseExpr(testScriptFull) } 90 | // property("testScriptFull") = secure { run(testScriptFull) } 91 | // property("valTest") = secure { parse(valTest) } 92 | // property("valTest") = secure { run (valTest) } 93 | // property("defTest") = secure { parse(defTest) } 94 | // property("defTest") = secure { run (defTest) } 95 | // def parseExpr(code:String): Boolean = 96 | // attemptT(p, truthfully(println(s"Parse Tree: ${WorldEditLang.parseExpr(Reader.read(code))}"))) 97 | // 98 | // def parse(code:String): Boolean = 99 | // attemptT(p, truthfully(println(s"Parse Tree: ${WorldEditLang.parse(code)}"))) 100 | -------------------------------------------------------------------------------- /other/minelang/src/test/scala/com/joshcough/minecraft/TestHelpers.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | import org.scalacheck.Properties 4 | import org.scalacheck.Prop 5 | import org.scalacheck.Prop._ 6 | 7 | trait TestHelpers { self: Properties => 8 | 9 | implicit val world = TestServer.world 10 | 11 | def trying(f: => Prop) = secure { 12 | try f catch { case e: Throwable => e.printStackTrace; throw e } 13 | } 14 | 15 | def test(name:String)(f: => Prop) = property(name) = trying(f) 16 | } 17 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 2 | -------------------------------------------------------------------------------- /project/build.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | import Keys._ 3 | import java.io.File 4 | import sbtassembly.Plugin._ 5 | import AssemblyKeys._ 6 | 7 | trait Common { 8 | 9 | val projectUrl = "https://github.com/joshcough/MinecraftPlugins" 10 | 11 | lazy val standardSettings = join( 12 | Defaults.defaultSettings, 13 | libDeps( 14 | "javax.servlet" % "servlet-api" % "2.5" % "provided->default", 15 | "org.scalacheck" %% "scalacheck" % "1.11.3" % "test", 16 | "org.bukkit" % "bukkit" % "1.7.2-R0.2" 17 | ), 18 | Seq( 19 | organization := "com.joshcough", 20 | version := "0.3.4", 21 | scalaVersion := "2.11.6", 22 | crossScalaVersions := Seq("2.10.4", "2.11.6"), 23 | licenses <++= version(v => Seq("MIT" -> url(projectUrl + "/blob/%s/LICENSE".format(v)))), 24 | publishMavenStyle := true, 25 | resolvers ++= Seq( 26 | Resolver.jcenterRepo, 27 | Resolver.sonatypeRepo("snapshots"), 28 | "Bukkit" at "http://repo.bukkit.org/content/groups/public/" 29 | ), 30 | traceLevel := 10 31 | //,logLevel := Level.Warn 32 | ) 33 | ) 34 | 35 | // the core plugin library 36 | lazy val core = Project( 37 | id = "core", 38 | base = file("core"), 39 | settings = join( 40 | standardSettings, 41 | copyPluginToBukkitSettings(None), 42 | named("scala-minecraft-plugin-api"), 43 | libDeps("org.scalacheck" %% "scalacheck" % "1.10.0" % "test") 44 | ) 45 | ) 46 | 47 | def copyPluginToBukkitSettings(meta: Option[String]) = Seq( 48 | // make publish local also copy jars to my bukkit server :) 49 | publishLocal <<= (packagedArtifacts, publishLocal) map { case (r, _) => 50 | r collectFirst { case (Artifact(_,"jar","jar", m, _, _, name), f) if m == meta => 51 | println("copying " + f.name + " to bukkit server") 52 | IO.copyFile(f, new File("bukkit/plugins/" + f.name)) 53 | } 54 | } 55 | ) 56 | 57 | def join(settings: Seq[Def.Setting[_]]*) = settings.flatten 58 | def named(pname: String) = Seq(name := pname) 59 | def libDeps(libDeps: sbt.ModuleID*) = Seq(libraryDependencies ++= libDeps) 60 | 61 | def pluginYmlSettings(pluginClassname: String, author: String): Seq[Setting[_]] = Seq[Setting[_]]( 62 | resourceGenerators in Compile <+= 63 | (resourceManaged in Compile, streams, productDirectories in Compile, dependencyClasspath in Compile, version, compile in Compile, runner) map { 64 | (dir, s, cp1, cp2, v, _, r) => 65 | Run.run( 66 | "com.joshcough.minecraft.YMLGenerator", (Attributed.blankSeq(cp1) ++ cp2).map(_.data), 67 | Seq(pluginClassname, author, v, dir.getAbsolutePath), 68 | s.log)(r) 69 | Seq(dir / "plugin.yml", dir / "config.yml") 70 | } 71 | ) 72 | 73 | } 74 | 75 | object build extends Build 76 | with Common 77 | with ExamplesBuild { 78 | 79 | // this is the main project, that builds all subprojects. 80 | // it doesnt contain any code itself. 81 | lazy val all = Project( 82 | id = "all", 83 | base = file("."), 84 | settings = standardSettings, 85 | aggregate = Seq( 86 | scalaLibPlugin, 87 | core, 88 | mineLang, 89 | //microExample, 90 | Arena, 91 | BanArrows, 92 | BlockChanger, 93 | BlockChangerGold, 94 | Farmer, 95 | GetOffMyLawn, 96 | God, 97 | LightningArrows, 98 | MultiPlayerCommands, 99 | NoRain, 100 | PluginCommander, 101 | Shock, 102 | TeleportBows, 103 | Thor, 104 | TreeDelogger, 105 | WorldEdit, 106 | YellowBrickRoad, 107 | ZombieApocalypse 108 | ) 109 | ) 110 | 111 | // this is just a convenience project 112 | // for me to easily publish my most used plugins to my bukkit server. 113 | // > sbt 'project commonPlugins' publishLocal 114 | lazy val commonPlugins = Project( 115 | id = "commonPlugins", 116 | base = file(".commonPlugins"), 117 | settings = standardSettings, 118 | aggregate = Seq( 119 | scalaLibPlugin, 120 | core, 121 | MultiPlayerCommands, 122 | PluginCommander, 123 | WorldEdit 124 | ) 125 | ) 126 | 127 | // this project supplies the scala language classes. 128 | // it is needed in the bukkit plugins dir to run any scala plugins. 129 | lazy val scalaLibPlugin = Project( 130 | id = "scalaLibPlugin", 131 | base = file("scala-lib-plugin"), 132 | settings = join( 133 | standardSettings, 134 | named("scala-minecraft-scala-library"), 135 | assemblySettings, 136 | copyPluginToBukkitSettings(Some("assembly")) 137 | ) 138 | ) 139 | 140 | // minelang is a plugin that contains a language i wrote that is much like clojure 141 | // and allows people to easily write plugins without having to deploy lots of crap. 142 | // however, this has more or less been replaced by erminecraft. 143 | lazy val mineLang = Project( 144 | id = "mineLang", 145 | base = file("other/minelang"), 146 | settings = join( 147 | standardSettings, 148 | pluginYmlSettings("com.joshcough.minecraft.MineLangPlugin", "JoshCough"), 149 | named("MineLang"), 150 | libDeps( 151 | "jline" % "jline" % "2.11", 152 | "org.clojure" % "clojure" % "1.4.0" 153 | ) 154 | ), 155 | dependencies = Seq(core) 156 | ) 157 | } 158 | 159 | trait ExamplesBuild extends Build with Common { 160 | // a special example project... 161 | lazy val microExample = Project(id = "microexample", base = file("microexample")) 162 | 163 | // a whole pile of example projects 164 | lazy val Arena = exampleProject("Arena") 165 | lazy val BanArrows = exampleProject("BanArrows") 166 | lazy val BlockChanger = exampleProject("BlockChanger") 167 | lazy val BlockChangerGold = exampleProject("BlockChangerGold") 168 | lazy val Farmer = exampleProject("Farmer") 169 | lazy val GetOffMyLawn = exampleProject("GetOffMyLawn") 170 | lazy val God = exampleProject("God") 171 | lazy val LightningArrows = exampleProject("LightningArrows") 172 | lazy val MultiPlayerCommands = exampleProject("MultiPlayerCommands") 173 | lazy val NoRain = exampleProject("NoRain") 174 | lazy val PluginCommander = exampleProject("PluginCommander") 175 | lazy val Shock = exampleProject("Shock") 176 | lazy val Thor = exampleProject("Thor") 177 | lazy val TeleportBows = exampleProject("TeleportBows") 178 | lazy val TreeDelogger = exampleProject("TreeDelogger") 179 | lazy val WorldEdit = exampleProject("WorldEdit") 180 | lazy val YellowBrickRoad = exampleProject("YellowBrickRoad") 181 | lazy val ZombieApocalypse = exampleProject("ZombieApocalypse") 182 | 183 | def exampleProject(exampleProjectName: String, deps: sbt.ModuleID*) = { 184 | val pluginClassname = "com.joshcough.minecraft.examples." + exampleProjectName 185 | Project( 186 | id = exampleProjectName, 187 | base = file("examples/" + exampleProjectName), 188 | settings = join( 189 | standardSettings, 190 | named(exampleProjectName), 191 | pluginYmlSettings(pluginClassname, "JoshCough"), 192 | copyPluginToBukkitSettings(None), 193 | libDeps(deps:_*) 194 | ), 195 | dependencies = Seq(core) 196 | ) 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | resolvers += Resolver.url( 2 | "Josh Cough sbt plugins", 3 | url("http://dl.bintray.com/content/joshcough/sbt-plugins"))( 4 | Resolver.ivyStylePatterns) 5 | 6 | resolvers ++= Seq( 7 | "Bukkit" at "http://repo.bukkit.org/content/groups/public/" 8 | ) 9 | 10 | addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") 11 | 12 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 13 | 14 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2") 15 | -------------------------------------------------------------------------------- /project/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("me.lessis" % "bintray-sbt" % "0.3.0") 2 | -------------------------------------------------------------------------------- /scala-lib-plugin/build.sbt: -------------------------------------------------------------------------------- 1 | import AssemblyKeys._ 2 | 3 | artifact in (Compile, assembly) ~= { art => art.copy(`classifier` = Some("assembly")) } 4 | 5 | addArtifact(artifact in (Compile, assembly), assembly) 6 | 7 | // TODO: plugin doc says "To exclude some jar file, first consider using "provided" dependency." 8 | // Figure out what that means... 9 | excludedJars in assembly <<= (fullClasspath in assembly) map { cp => 10 | cp filter {_.data.getName.contains("craftbukkit")} 11 | } -------------------------------------------------------------------------------- /scala-lib-plugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: ScalaLibPlugin 2 | main: com.joshcough.minecraft.ScalaLibPlugin 3 | author: Josh Cough 4 | version: 2.11.0 5 | database: false 6 | -------------------------------------------------------------------------------- /scala-lib-plugin/src/main/scala/com/joshcough/minecraft/ScalaLibPlugin.scala: -------------------------------------------------------------------------------- 1 | package com.joshcough.minecraft 2 | 3 | /** 4 | * This plugin and this code is not intended for use. 5 | * It is just an empty plugin that is required for turning this library 6 | * into a plugin, so that the API and Scala can be on the classpath for 7 | * plugins that want to use this API. 8 | */ 9 | class ScalaLibPlugin extends org.bukkit.plugin.java.JavaPlugin { 10 | override def onEnable : Unit = {} 11 | override def onDisable : Unit = {} 12 | } 13 | --------------------------------------------------------------------------------