├── .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 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/compiler.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 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/highlighting.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
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 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/scala_compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/scala_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 | [](https://travis-ci.org/joshcough/MinecraftPlugins)
2 | [](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 |
--------------------------------------------------------------------------------