├── .gitignore
├── ContainerManager.iml
├── Dockerfile
├── DockerizedCraft.iml
├── LICENSE
├── README.md
├── docker-compose.yml
├── docs
├── README.bbcode
├── container-manager-demo.gif
├── logo-small.png
└── logo.png
├── pom.xml
├── scripts
└── kubernetes
│ └── deployment.yaml
└── src
└── main
├── java
└── de
│ └── craftmania
│ └── dockerizedcraft
│ ├── DockerizedCraft.java
│ ├── connection
│ └── balancer
│ │ ├── BalancedReconnectHandler.java
│ │ ├── ConnectionBalancer.java
│ │ ├── command
│ │ └── GroupCommand.java
│ │ ├── model
│ │ └── Group.java
│ │ ├── session
│ │ ├── RedisSessionStorage.java
│ │ └── SessionStorage.java
│ │ └── strategy
│ │ ├── BalanceStrategy.java
│ │ └── Strategy.java
│ ├── container
│ └── inspector
│ │ ├── IContainerInspector.java
│ │ ├── docker
│ │ ├── DockerClientFactory.java
│ │ ├── DockerContainerInspector.java
│ │ └── ResultCallback.java
│ │ ├── events
│ │ └── ContainerEvent.java
│ │ └── kubernetes
│ │ ├── KubernetesContainerInspector.java
│ │ └── PodWatcher.java
│ ├── plugin
│ └── notifier
│ │ ├── AbstractNotifier.java
│ │ └── serverlist
│ │ └── ServerListPluginNotifier.java
│ └── server
│ └── updater
│ ├── ServerUpdater.java
│ └── events
│ ├── PostAddServerEvent.java
│ ├── PostRemoveServerEvent.java
│ ├── PreAddServerEvent.java
│ └── PreRemoveServerEvent.java
└── resources
├── bungee.yml
├── connection-balancer.yml
├── container-inspector.yml
├── plugin-notifier.yml
└── server-updater.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | target
26 |
27 | .idea
28 |
--------------------------------------------------------------------------------
/ContainerManager.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 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM maven as build
2 |
3 | WORKDIR /build
4 | COPY src /build/src
5 | COPY pom.xml /build/
6 | RUN mvn package
7 |
8 | FROM itzg/bungeecord
9 |
10 | COPY --from=build /build/target/assembly/DockerizedCraft.jar /server/plugins/DockerizedCraft.jar
11 |
--------------------------------------------------------------------------------
/DockerizedCraft.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 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Alexander Mührenberg
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | Dockerized Craft - Core
4 | ======================
5 |
6 | __Never maintain your Bungeecord manually again! Automatically listens to Docker events and adds servers to BungeeCord.__
7 |
8 | Additionally supporting plugin messaging, connection balancing and proxy server list updates.
9 |
10 | 
11 |
12 | ## Container Inspector
13 |
14 | ### Features
15 |
16 | - Automatically listens on docker events
17 | - Extracts environment variables from containers
18 | - Triggers BungeeCord Custom events on Docker container events
19 |
20 | ### Configuration
21 |
22 | Check [container-inspector.yml](/src/main/resources/container-inspector.yml)
23 |
24 | ## Server Updater
25 |
26 | ### Features
27 |
28 | - Automatically adds Server to Bungeecord
29 | - Automatically removes Server to Bungeecord
30 | - Configurable add/remove actions
31 | - Supports Health Checks
32 |
33 | ### Configuration
34 |
35 | Check [server-updater.yml](/src/main/resources/server-updater.yml)
36 |
37 | ## Connection Balancer
38 |
39 | ### Features
40 |
41 | - Server Groups
42 | - Default connection group (Fallback/Default servers)
43 | - Forced hosts for groups (i.e. eu lobbies and us lobbies)
44 | - Listens on Container events to add servers to groups based on their environment variables (i.e SERVER_GROUP=lobby)
45 | - Connection Balancing Strategies per group
46 | - balanced: Connect Players to the Server of the group with the fewest players
47 | - More will follow!
48 | - Does not overwrite restrictions!
49 |
50 |
51 | ### Configuration
52 |
53 | Check [connection-balancer.yml](/src/main/resources/connection-balancer.yml)
54 |
55 |
56 | ## Plugin Notifier
57 |
58 | ### Server List Payload
59 |
60 | Send Server information to the single servers.
61 | This can be used to add Sever Selectors etc.
62 |
63 | Add as many information as you want by easy to use environment variable mapping.
64 |
65 | __Example Payload__ (Channel: ContainerManager, Subchannel: ServerData)
66 |
67 | ````json
68 | {
69 | "us-lobby-1":{
70 | "address":"172.19.0.5:25565",
71 | "host":"172.19.0.5",
72 | "port":25565,
73 | "motd":"A Minecraft Server Instance",
74 | "name":"us-lobby-1",
75 | "proxied_players":12,
76 | "type":"spigot",
77 | "category":"us-lobby",
78 | "tags":"some,awesome,tags"
79 | },
80 | "eu-lobby-2":{
81 | "address":"172.19.0.4:25565",
82 | "host":"172.19.0.4",
83 | "port":25565,
84 | "motd":"A Minecraft Server Instance",
85 | "name":"eu-lobby-2",
86 | "proxied_players":5,
87 | "type":"spigot",
88 | "category":"eu-lobby",
89 | "tags":""
90 | },
91 | "eu-lobby-1":{
92 | "address":"172.19.0.3:25565",
93 | "host":"172.19.0.3",
94 | "port":25565,
95 | "motd":"A Minecraft Server Instance",
96 | "name":"eu-lobby-1",
97 | "proxied_players":0,
98 | "type":"spigot",
99 | "category":"eu-lobby",
100 | "tags":""
101 | }
102 | }
103 | ````
104 |
105 | ### Configuration
106 |
107 | Check [plugin-notifier.yml](/src/main/resources/plugin-notifier.yml)
108 |
109 | ## Try it yourself
110 |
111 | 1. Checkout the repository
112 | 2. Build the .jar with maven or copy an attached one from the last releases.
113 | 3. run `docker-compose --project-name minecraft up -d`
114 | 4. Wait until all containers did start and connect to localhost with you Minecraft client
115 |
116 | ## Todo's
117 |
118 | - Reduce .jar size
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 | services:
3 | bungeecord:
4 | image: itzg/bungeecord
5 | environment:
6 | - UID=1000
7 | ports:
8 | - 25565:25577
9 | volumes:
10 | - ./target/assembly/DockerizedCraft.jar:/server/plugins/DockerizedCraft.jar
11 | - //var/run/docker.sock:/var/run/docker.sock
12 | networks:
13 | - local
14 |
15 | redis:
16 | image: bitnami/redis:latest
17 | environment:
18 | - ALLOW_EMPTY_PASSWORD=yes
19 | networks:
20 | - local
21 |
22 | eu-lobby-1:
23 | image: itzg/minecraft-server
24 | environment:
25 | - VERSION=1.12.2
26 | - TYPE=spigot
27 | - SERVER_PORT=25565
28 | - SERVER_NAME=eu-lobby-1
29 | - SERVER_GROUP=eu-lobby
30 | - SERVER_CATEGORY=eu-lobby
31 | - ONLINE_MODE=FALSE
32 | - EULA=true
33 | networks:
34 | - local
35 |
36 | eu-lobby-2:
37 | image: itzg/minecraft-server
38 | environment:
39 | - VERSION=1.12.2
40 | - TYPE=spigot
41 | - SERVER_PORT=25565
42 | - SERVER_NAME=eu-lobby-2
43 | - SERVER_GROUP=eu-lobby
44 | - SERVER_CATEGORY=eu-lobby
45 | - ONLINE_MODE=FALSE
46 | - EULA=true
47 | networks:
48 | - local
49 |
50 | us-lobby-1:
51 | image: itzg/minecraft-server
52 | environment:
53 | - VERSION=1.12.2
54 | - TYPE=spigot
55 | - SERVER_PORT=25565
56 | - SERVER_NAME=us-lobby-1
57 | - SERVER_GROUP=us-lobby
58 | - SERVER_CATEGORY=us-lobby
59 | - SERVER_TAGS=some,awesome,tags
60 | - ONLINE_MODE=FALSE
61 | - EULA=true
62 | networks:
63 | - local
64 | networks:
65 | local:
--------------------------------------------------------------------------------
/docs/README.bbcode:
--------------------------------------------------------------------------------
1 | [B][SIZE=6][COLOR=rgb(255, 128, 0)]DockerizedCraft - Core[/COLOR][/SIZE][/B]
2 |
3 | [IMG]https://github.com/DockerizedCraft/Core/raw/master/docs/logo-small.png[/IMG]
4 |
5 | [B]Never maintain your Bungeecord manually again! Automatically listens to Docker events and adds servers to BungeeCord.[/B]
6 |
7 | Additionally supporting plugin messaging, connection balancing and proxy server list updates.
8 |
9 | [IMG]https://raw.githubusercontent.com/DockerizedCraft/ContainerManager/master/docs/container-manager-demo.gif[/IMG]
10 |
11 | [SIZE=5][COLOR=#ff8000][B]Container Inspector[/B][/COLOR][/SIZE]
12 |
13 | [SIZE=4][B]Features[/B][/SIZE]
14 |
15 | [LIST]
16 | [*]Automatically listens on docker events
17 | [*]Extracts environment variables from containers
18 | [*]Triggers BungeeCord Custom events on Docker container events
19 | [/LIST]
20 |
21 | [SIZE=4][B]Configuration[/B][/SIZE]
22 |
23 | [SPOILER="container-inspector.yml"][code=YAML]# Is the docker listener enabled? This is the core functionality and should stay enabled
24 | # Type: Boolean
25 | enabled: true
26 |
27 | # Outputs extra information
28 | # Type: Boolean
29 | debug: true
30 |
31 | # Ensure the read permissions
32 | # available schemas: tcp or unix
33 | # Type: String
34 | host: unix:///var/run/docker.sock
35 |
36 | # Type: Boolean
37 | tsl-verify: false
38 |
39 | # Type: String|null
40 | cert-path: ~
41 |
42 | # Type: Section
43 | registry:
44 | # Type: String|null
45 | username: ~
46 | # Type: String|null
47 | password: ~
48 | # Type: String|null
49 | email: ~
50 | # Type: String|null
51 | url: ~
52 |
53 | # The network name to resolve IP addresses
54 | # Only one network is possible to avoid confusion about which ip to use
55 | # check your networks with `docker network ls`
56 | network: "minecraft_local"
57 | [/code][/SPOILER]
58 |
59 | [SIZE=5][COLOR=#ff8000][B]Server Updater[/B][/COLOR][/SIZE]
60 |
61 | [SIZE=4][B]Features[/B][/SIZE]
62 |
63 | [LIST]
64 | [*]Automatically adds servers to Bungeecord
65 | [*]Automatically removes servers from Bungeecord
66 | [*]Configurable add/remove actions
67 | [*]Supports Health Checks
68 | [/LIST]
69 |
70 | [SIZE=4][B]Configuration[/B][/SIZE]
71 |
72 | [SPOILER="server-updater.yml"][code=YAML]# Is the Server update enabled?
73 | # Type: Boolean
74 | enabled: true
75 |
76 | # Extra output
77 | # Type: Boolean
78 | debug: false
79 |
80 | # Container events actions to listen on for adding server
81 | # i.e.: "start", "health-status: healthy"
82 | # Recommended "start", "bootstrap" and health checks
83 | # the "bootstrap" events is triggered when connectionbalancer starts to register all running containers. Should ne be removed
84 | # If you want to add only healthy containers be aware of removing "start" action
85 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
86 | # Type: List
87 | add-actions:
88 | - "bootstrap"
89 | - "start"
90 | - "unpause"
91 |
92 | # Container events actions to listen on for remving server
93 | # i.e.: "kill", "die"
94 | # Recommended "die" and health checks
95 | # If you want to remove unhealthy containers add i.e: "health_status: unhealthy"
96 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
97 | # Type: List
98 | remove-actions:
99 | - "kill"
100 | - "die"
101 | - "stop"
102 | - "pause"
103 |
104 |
105 |
106 | # Environment variables of the containers
107 | # Type: Section
108 | environment-variables:
109 | # The events listener will only add server with the defined environment variable
110 | # ie. docker run -e SERVER_TYPE=minecraft_spigot my_server
111 | # Type: String
112 | identifier: SERVER_TYPE
113 |
114 | # Be default the first exposed port is taken if the container exposes multiple ports you can
115 | # set it by setting the PORT environment variable in the container
116 | # If you exposed multiple ports its highly recommended to set the environment variable
117 | # Type: String
118 | port: SERVER_PORT
119 |
120 | # Setting the motd in the Bungeecord setting
121 | # i.e. docker run -e SERVER_FORCED_HOST="Another Minecraft Server" playerworld:latest
122 | # Type: String
123 | motd: SERVER_MOTD
124 |
125 | # Setting the server to restricted
126 | # If not set it is false, only excepts: "restricted" or "unrestricted"
127 | # i.e. docker run -e SERVER_RESTRICTED=true playerworld:latest
128 | # Type: String
129 | restricted: SERVER_RESTRICTED
130 |
131 | # Each server name needs to be unique
132 | # If you are not able to control if it is unique (autoscaling or whatever) you should not set it in your container
133 | # If you do not set the environment variable the container name itself will be used
134 | # Two server with the same name will overwrite each other
135 | # Type: String
136 | name: SERVER_NAME
137 | [/code][/SPOILER]
138 |
139 | [SIZE=5][COLOR=#ff8000][B]Connection Balancer[/B][/COLOR][/SIZE]
140 |
141 | [SIZE=4][B]Features[/B][/SIZE]
142 |
143 | [LIST]
144 | [*]Server Groups
145 | [*]Default connection group (Fallback/Default servers)
146 | [*]Join Group Commands (like /lobby, /hub or /game-xy)
147 | [*]Forced hosts for groups (i.e. eu lobbies and us lobbies)
148 | [*]Listens on Container events to add servers to groups based on their environment variables (i.e SERVER_GROUP=lobby)
149 | [*]Connection Balancing Strategies per group
150 | [LIST]
151 | [*]balanced: Connect Players to the Server of the group with the fewest players
152 | [*]More will follow!
153 | [/LIST]
154 | [*]Does not overwrite restrictions!
155 | [*]Stores Player sessions in Redis to handle reconnections
156 | [/LIST]
157 |
158 | [SIZE=4][B]Configuration[/B][/SIZE]
159 |
160 | [SPOILER="connection-balancer.yml"][code=YAML]# Care if you disable it you will need to configure default, priority and fallback servers by hand
161 | # Or use an different connection balancer handler plugin (Is it compatible?)
162 | # Type: Boolean
163 | enabled: true
164 |
165 | # Outputs extra information
166 | # Type: Boolean
167 | debug: true
168 |
169 | # To store player session for the reconnect handler
170 | # Type: Section
171 | session-store:
172 | # Type: Section
173 | redis:
174 | # Type: String
175 | host: "redis"
176 | # Type: String
177 | password: ~
178 | # Type: Integer
179 | port: 6379
180 | # Type: Boolean
181 | ssl: true
182 |
183 | # Environment variables of the containers
184 | # Type: Section
185 | environment-variables:
186 | # If the environment variable is set the server will be added to the priority list of connectionbalancer
187 | # This plugin implemented a custom load balancer which will use defined groups
188 | # Leaving the env variable blank will add the server to the default group
189 | # Type: String
190 | group: SERVER_GROUP
191 |
192 | # To enable forced host for the single instance.
193 | # You can also configure a forced host for a whole group. See groups config. I would recommend to do so events with single instances
194 | # i.e. docker run -e SERVER_FORCED_HOST=muehre.craftmania.de playerworld:latest
195 | # Type: String
196 | forced-host: SERVER_FORCED_HOST
197 |
198 | # Type: Section
199 | # check docker.event_listener.environment_variables.group_key
200 | groups:
201 | # Server group configuration
202 | # default group is used if a a container does not have a group environment variable. You can also configure it here
203 | # Type: Section
204 | # Group:
205 | # - strategy(String): balance is the only strategy atm. I will implement more as soon as they are required
206 | # - forced_host(String) Optional: people joining over this host will be send to this group
207 | # - can-reconnect(Boolean) Default: false: if a player can reconnect to this group. Usefull to disable for i.e minigames
208 | # - restricted(Boolean) Default: false: Is a permission required?
209 | eu-lobby:
210 | strategy: balance
211 | can-reconnect: true
212 | restricted: false
213 |
214 | game-xy:
215 | strategy: balance
216 | can-reconnect: false
217 | restricted: false
218 |
219 | private:
220 | strategy: balance
221 | can-reconnect: true
222 | restricted: false
223 |
224 | us-lobby:
225 | strategy: balance
226 | can-reconnect: true
227 | restricted: false
228 |
229 | # The default group a user is connected to if he freshly joins or his group was restricted in re-connections.
230 | # And not forced host is matching
231 | # Type: String
232 | default-group: eu-lobby
233 |
234 | # Commands that players can execute to join a certain group
235 | # Type Section
236 | join-commands:
237 | lobby: eu-lobby
238 | hub: eu-lobby
239 |
240 | # Setting force hosts. Use {dot} placeholder for dots to not break yaml syntax
241 | # Type: Section
242 | forced-hosts:
243 | "us{dot}mynetwork{dot}net": "us-lobby"
244 | "eu{dot}mynetwork{dot}net": "eu-lobby"
245 | [/code][/SPOILER]
246 |
247 | [SIZE=5][COLOR=#ff8000][B]Plugin Notifier[/B][/COLOR][/SIZE]
248 |
249 | [SIZE=4][B]Server List Payload[/B][/SIZE]
250 |
251 | Send Server information to the single servers. This can be used to add Sever Selectors etc.
252 |
253 | Add as many information as you want by easy to use environment variable mapping.
254 |
255 | [B]Example Payload [/B](Channel: ContainerManager, Subchannel: ServerData)
256 |
257 | [code]{
258 | "us-lobby-1":{
259 | "address":"172.19.0.5:25565",
260 | "host":"172.19.0.5",
261 | "port":25565,
262 | "motd":"A Minecraft Server Instance",
263 | "name":"us-lobby-1",
264 | "proxied_players":12,
265 | "type":"spigot",
266 | "category":"us-lobby",
267 | "tags":"some,awesome,tags"
268 | },
269 | "eu-lobby-2":{
270 | "address":"172.19.0.4:25565",
271 | "host":"172.19.0.4",
272 | "port":25565,
273 | "motd":"A Minecraft Server Instance",
274 | "name":"eu-lobby-2",
275 | "proxied_players":5,
276 | "type":"spigot",
277 | "category":"eu-lobby",
278 | "tags":""
279 | },
280 | "eu-lobby-1":{
281 | "address":"172.19.0.3:25565",
282 | "host":"172.19.0.3",
283 | "port":25565,
284 | "motd":"A Minecraft Server Instance",
285 | "name":"eu-lobby-1",
286 | "proxied_players":0,
287 | "type":"spigot",
288 | "category":"eu-lobby",
289 | "tags":""
290 | }
291 | }[/code]
292 |
293 | [B]Configuration[/B]
294 |
295 | [SPOILER="plugin-notifier.yml"][code=YAML]# If you disable it no plugin which depends on this data will work
296 | # Type: Boolean
297 | enabled: true
298 |
299 | # Outputs extra information
300 | # Type: Boolean
301 | debug: true
302 |
303 | # Server updates will be sent after any add or remove anyway but the interval is usefull to update i.e. ProxiedPlayer Information
304 | # Type: Integer
305 | refresh-interval: 30
306 |
307 | # Maps environment variables and forwards them with the specified key to the bukkit client plugin
308 | # ie. docker run -e CATEGORY=factions
309 | # Reserved keys as they will be handed down anyway: name, address, motd, restricted (In case you want to overwrite you can do so)
310 | # Type: Section
311 | # Type MetaDataConfig:
312 | # - environment-variable (string): The environemnt variable to access
313 | # - required (bool): Is this value required? If not given and no default
314 | # is defined the server will not be added to connectionbalancer
315 | # - default (string): If the environment variable is not defined will fall
316 | # back to the given default
317 | meta-data-mapper:
318 | # make the SERVER_TYPE also accessible
319 | type:
320 | required: true
321 | environment-variable: SERVER_TYPE
322 |
323 | # Category for i.e create server selector menus based on categories
324 | category:
325 | environment-variable: SERVER_CATEGORY
326 | required: true
327 | default: "none"
328 |
329 | tags:
330 | environment-variable: SERVER_TAGS
331 | required: true
332 | default: ""
333 | [/code][/SPOILER]
334 |
335 | [SIZE=5][COLOR=#ff8000][B]Try it yourself[/B][/COLOR][/SIZE]
336 |
337 | [LIST=1]
338 | [*]Checkout the repository
339 | [*]Build the .jar with maven or copy an attached one from the last releases.
340 | [*]run docker-compose --project-name minecraft up -d
341 | [*]Wait until all containers did start and connect to localhost with you Minecraft client
342 | [/LIST]
343 |
344 | [SIZE=5][COLOR=#ff8000][B]Todo's[/B][/COLOR][/SIZE]
345 |
346 | [LIST]
347 | [*]Reduce .jar size
348 | [/LIST]
--------------------------------------------------------------------------------
/docs/container-manager-demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DockerizedCraft/Core/65a6ae9c75e075870ecbf850678d7da6edf9140d/docs/container-manager-demo.gif
--------------------------------------------------------------------------------
/docs/logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DockerizedCraft/Core/65a6ae9c75e075870ecbf850678d7da6edf9140d/docs/logo-small.png
--------------------------------------------------------------------------------
/docs/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DockerizedCraft/Core/65a6ae9c75e075870ecbf850678d7da6edf9140d/docs/logo.png
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | de.craftmania
8 | DockerizedCraft
9 | 0.2.2
10 |
11 |
12 |
13 |
14 | org.apache.maven.plugins
15 | maven-shade-plugin
16 | 3.1.1
17 |
18 |
19 | package
20 |
21 | shade
22 |
23 |
24 | target/assembly/${project.artifactId}.jar
25 |
26 |
27 | org.bstats
28 | de.craftmania.dockerizedcraft
29 |
30 |
31 |
32 |
33 |
34 |
35 | true
36 | false
37 | false
38 |
39 |
40 | *:*
41 |
42 | META-INF/*.SF
43 | META-INF/*.DSA
44 | META-INF/*.RSA
45 |
46 |
47 |
48 |
49 |
50 |
51 | org.apache.maven.plugins
52 | maven-compiler-plugin
53 |
54 | 8
55 | 8
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | bungeecord-repo
64 | https://oss.sonatype.org/content/repositories/snapshots
65 |
66 |
67 | CodeMC
68 | https://repo.codemc.org/repository/maven-public
69 |
70 |
71 |
72 |
73 |
74 | net.md-5
75 | bungeecord-api
76 | 1.15-SNAPSHOT
77 | jar
78 | provided
79 |
80 |
81 | net.md-5
82 | bungeecord-api
83 | 1.15-SNAPSHOT
84 | javadoc
85 | provided
86 |
87 |
88 | com.google.code.gson
89 | gson
90 | 2.8.6
91 |
92 |
93 | io.fabric8
94 | kubernetes-client
95 | 4.8.0
96 |
97 |
98 | com.github.docker-java
99 | docker-java
100 | 3.1.5
101 | compile
102 |
103 |
104 | org.slf4j
105 | slf4j-log4j12
106 | 2.0.0-alpha1
107 |
108 |
109 | redis.clients
110 | jedis
111 | 3.2.0
112 | jar
113 | compile
114 |
115 |
116 | org.bstats
117 | bstats-bungeecord
118 | 1.7
119 | compile
120 |
121 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/scripts/kubernetes/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: minecraft
5 | ---
6 | apiVersion: rbac.authorization.k8s.io/v1
7 | kind: Role
8 | metadata:
9 | namespace: minecraft
10 | name: dockerizedcraft-pod-reader
11 | rules:
12 | - apiGroups: [""]
13 | resources: ["pods"]
14 | verbs: ["get", "watch", "list"]
15 | ---
16 | apiVersion: v1
17 | kind: ServiceAccount
18 | metadata:
19 | namespace: minecraft
20 | name: dockerizedcraft-account
21 | ---
22 | apiVersion: rbac.authorization.k8s.io/v1
23 | kind: RoleBinding
24 | metadata:
25 | name: dockerizedcraft-pod-reader-rolebinding
26 | namespace: minecraft
27 | subjects:
28 | - kind: ServiceAccount
29 | name: dockerizedcraft-account
30 | namespace: minecraft
31 | roleRef:
32 | kind: Role
33 | name: dockerizedcraft-pod-reader
34 | apiGroup: rbac.authorization.k8s.io
35 | ---
36 | apiVersion: v1
37 | data:
38 | connection-balancer.yml: |-
39 | # Care if you disable it you will need to configure default, priority and fallback servers by hand
40 | # Or use an different connection balancer handler plugin (Is it compatible?)
41 | # Type: Boolean
42 | enabled: true
43 |
44 | # Outputs extra information
45 | # Type: Boolean
46 | debug: true
47 |
48 | # To store player session for the reconnect handler
49 | # Type: Section
50 | session-store:
51 | # Type: Section
52 | redis:
53 | # Type: String
54 | host: "redis"
55 | # Type: String
56 | password: ~
57 | # Type: Integer
58 | port: 6379
59 | # Type: Boolean
60 | ssl: true
61 |
62 | # Environment variables of the containers
63 | # Type: Section
64 | environment-variables:
65 | # If the environment variable is set the server will be added to the priority list of connectionbalancer
66 | # This plugin implemented a custom load balancer which will use defined groups
67 | # Leaving the env variable blank will add the server to the default group
68 | # Type: String
69 | group: SERVER_GROUP
70 |
71 | # To enable forced host for the single instance.
72 | # You can also configure a forced host for a whole group. See groups config. I would recommend to do so events with single instances
73 | # i.e. docker run -e SERVER_FORCED_HOST=muehre.craftmania.de playerworld:latest
74 | # Type: String
75 | forced-host: SERVER_FORCED_HOST
76 |
77 | # Type: Section
78 | # check docker.event_listener.environment_variables.group_key
79 | groups:
80 | # Server group configuration
81 | # default group is used if a a container does not have a group environment variable. You can also configure it here
82 | # Type: Section
83 | # Group:
84 | # - strategy(String): balance is the only strategy atm. I will implement more as soon as they are required
85 | # - forced_host(String) Optional: people joining over this host will be send to this group
86 | # - can-reconnect(Boolean) Default: false: if a player can reconnect to this group. Usefull to disable for i.e minigames
87 | # - restricted(Boolean) Default: false: Is a permission required?
88 | lobby:
89 | strategy: balance
90 | can-reconnect: true
91 | restricted: false
92 |
93 | server:
94 | strategy: balance
95 | can-reconnect: false
96 | restricted: false
97 |
98 | # The default group a user is connected to if he freshly joins or his group was restricted in re-connections.
99 | # And not forced host is matching
100 | # Type: String
101 | default-group: lobby
102 |
103 | # Commands that players can execute to join a certain group
104 | # Type Section
105 | join-commands:
106 | lobby: lobby
107 | hub: lobby
108 |
109 | # Setting force hosts. Use {dot} placeholder for dots to not break yaml syntax
110 | # Type: Section
111 | forced-hosts:
112 | # "us{dot}mynetwork{dot}net": "us-lobby"
113 | # "eu{dot}mynetwork{dot}net": "eu-lobby"
114 | container-inspector.yml: |-
115 | # Is the docker listener enabled? This is the core functionality and should stay enabled
116 | # Type: Boolean
117 | enabled: true
118 |
119 | # Outputs extra information
120 | # Type: Boolean
121 | debug: true
122 |
123 | # Backend type (docker or kubernetes)
124 | backend: kubernetes
125 |
126 | # Ensure the read permissions
127 | # available schemas: tcp or unix
128 | # Type: String
129 | docker:
130 | host: unix:///var/run/docker.sock
131 |
132 | # Type: Boolean
133 | tsl-verify: false
134 |
135 | # Type: String|null
136 | cert-path: ~
137 |
138 | # Type: Section
139 | registry:
140 | # Type: String|null
141 | username: ~
142 | # Type: String|null
143 | password: ~
144 | # Type: String|null
145 | email: ~
146 | # Type: String|null
147 | url: ~
148 |
149 | # The network name to resolve IP addresses
150 | # Only one network is possible to avoid confusion about which ip to use
151 | # check your networks with `docker network ls`
152 | network: "minecraft_local"
153 |
154 | kubernetes:
155 | # The namespace that bungeecord and all minecraft servers reside in
156 | namespace: minecraft
157 | plugin-notifier.yml: |-
158 | # If you disable it no plugin which depends on this data will work
159 | # Type: Boolean
160 | enabled: true
161 |
162 | # Outputs extra information
163 | # Type: Boolean
164 | debug: true
165 |
166 | # Server updates will be sent after any add or remove anyway but the interval is usefull to update i.e. ProxiedPlayer Information
167 | # Type: Integer
168 | refresh-interval: 30
169 |
170 | # Maps environment variables and forwards them with the specified key to the bukkit client plugin
171 | # ie. docker run -e CATEGORY=factions
172 | # Reserved keys as they will be handed down anyway: name, address, motd, restricted (In case you want to overwrite you can do so)
173 | # Type: Section
174 | # Type MetaDataConfig:
175 | # - environment-variable (string): The environemnt variable to access
176 | # - required (bool): Is this value required? If not given and no default
177 | # is defined the server will not be added to connectionbalancer
178 | # - default (string): If the environment variable is not defined will fall
179 | # back to the given default
180 | meta-data-mapper:
181 | # make the SERVER_TYPE also accessible
182 | type:
183 | required: true
184 | environment-variable: SERVER_TYPE
185 |
186 | # Category for i.e create server selector menus based on categories
187 | category:
188 | environment-variable: SERVER_CATEGORY
189 | required: true
190 | default: "none"
191 |
192 | tags:
193 | environment-variable: SERVER_TAGS
194 | required: true
195 | default: ""
196 | server-updater.yml: |-
197 | # Is the Server update enabled?
198 | # Type: Boolean
199 | enabled: true
200 |
201 | # Extra output
202 | # Type: Boolean
203 | debug: false
204 |
205 | # Container events actions to listen on for adding server
206 | # i.e.: "start", "health-status: healthy"
207 | # Recommended "start", "bootstrap" and health checks
208 | # the "bootstrap" events is triggered when connectionbalancer starts to register all running containers. Should ne be removed
209 | # If you want to add only healthy containers be aware of removing "start" action
210 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
211 | # Type: List
212 | add-actions:
213 | - "bootstrap"
214 | - "start"
215 | - "health_status: healthy"
216 | - "unpause"
217 | - "ADDED"
218 |
219 | # Container events actions to listen on for remving server
220 | # i.e.: "kill", "die"
221 | # Recommended "die" and health checks
222 | # If you want to remove unhealthy containers add i.e: "health_status: unhealthy"
223 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
224 | # Type: List
225 | remove-actions:
226 | - "kill"
227 | - "die"
228 | - "stop"
229 | - "pause"
230 | - "MODIFIED"
231 | - "PENDING"
232 | - "DELETED"
233 |
234 |
235 |
236 | # Environment variables of the containers
237 | # Type: Section
238 | environment-variables:
239 | # The events listener will only add server with the defined environment variable
240 | # ie. docker run -e SERVER_TYPE=minecraft_spigot my_server
241 | # Type: String
242 | identifier: SERVER_TYPE
243 |
244 | # Be default the first exposed port is taken if the container exposes multiple ports you can
245 | # set it by setting the PORT environment variable in the container
246 | # If you exposed multiple ports its highly recommended to set the environment variable
247 | # Type: String
248 | port: SERVER_PORT
249 |
250 | # Setting the motd in the Bungeecord setting
251 | # i.e. docker run -e SERVER_FORCED_HOST="Another Minecraft Server" playerworld:latest
252 | # Type: String
253 | motd: SERVER_MOTD
254 |
255 | # Setting the server to restricted
256 | # If not set it is false, only excepts: "restricted" or "unrestricted"
257 | # i.e. docker run -e SERVER_RESTRICTED=true playerworld:latest
258 | # Type: String
259 | restricted: SERVER_RESTRICTED
260 |
261 | # Each server name needs to be unique
262 | # If you are not able to control if it is unique (autoscaling or whatever) you should not set it in your container
263 | # If you do not set the environment variable the container name itself will be used
264 | # Two server with the same name will overwrite each other
265 | # Type: String
266 | name: SERVER_NAME
267 | kind: ConfigMap
268 | metadata:
269 | name: dockerizedcraft-config
270 | namespace: minecraft
271 | ---
272 | apiVersion: extensions/v1beta1
273 | kind: Deployment
274 | metadata:
275 | name: bungeecord
276 | namespace: minecraft
277 | spec:
278 | replicas: 1
279 | selector:
280 | matchLabels:
281 | dockerizedcraft/set: bungeecord
282 | template:
283 | metadata:
284 | labels:
285 | dockerizedcraft/set: bungeecord
286 | spec:
287 | containers:
288 | - env:
289 | - name: UID
290 | value: "1000"
291 | image: itzg/bungeecord
292 | imagePullPolicy: Always
293 | name: bungeecord
294 | ports:
295 | - containerPort: 25577
296 | hostPort: 25565
297 | name: 25577tcp255650
298 | protocol: TCP
299 | volumeMounts:
300 | - mountPath: /server/plugins/DockerizedCraft.jar
301 | name: bungeecord-claim-dockerizedcraft
302 | subPath: DockerizedCraft.jar
303 | - mountPath: /server/plugins/DockerizedCraft/
304 | name: dockerizedcraft-config
305 | serviceAccount: dockerizedcraft-account
306 | serviceAccountName: dockerizedcraft-account
307 | volumes:
308 | - name: bungeecord-claim-dockerizedcraft
309 | persistentVolumeClaim:
310 | claimName: bungeecord-claim-dockerizedcraft
311 | - configMap:
312 | defaultMode: 256
313 | name: dockerizedcraft-config
314 | optional: false
315 | name: dockerizedcraft-config
316 | ---
317 | apiVersion: extensions/v1beta1
318 | kind: Deployment
319 | metadata:
320 | name: lobby
321 | namespace: minecraft
322 | spec:
323 | replicas: 1
324 | selector:
325 | matchLabels:
326 | dockerizedcraft/set: lobby
327 | template:
328 | metadata:
329 | labels:
330 | dockerizedcraft/enabled: "true"
331 | dockerizedcraft/set: lobby
332 | spec:
333 | containers:
334 | - env:
335 | - name: EULA
336 | value: "true"
337 | - name: ONLINE_MODE
338 | value: "FALSE"
339 | - name: SERVER_GROUP
340 | value: lobby
341 | - name: SERVER_PORT
342 | value: "25565"
343 | - name: SERVER_TYPE
344 | value: spigot
345 | - name: VERSION
346 | value: 1.12.2
347 | image: itzg/minecraft-server
348 | imagePullPolicy: Always
349 | name: lobby
350 | ---
351 | apiVersion: extensions/v1beta1
352 | kind: Deployment
353 | metadata:
354 | name: server
355 | namespace: minecraft
356 | spec:
357 | replicas: 5
358 | selector:
359 | matchLabels:
360 | dockerizedcraft/set: server
361 | template:
362 | metadata:
363 | labels:
364 | dockerizedcraft/enabled: "true"
365 | dockerizedcraft/set: server
366 | spec:
367 | containers:
368 | - env:
369 | - name: EULA
370 | value: "true"
371 | - name: ONLINE_MODE
372 | value: "FALSE"
373 | - name: SERVER_GROUP
374 | value: server
375 | - name: SERVER_PORT
376 | value: "25565"
377 | - name: SERVER_TYPE
378 | value: spigot
379 | - name: VERSION
380 | value: 1.12.2
381 | image: itzg/minecraft-server
382 | imagePullPolicy: Always
383 | name: server
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/DockerizedCraft.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft;
2 |
3 | import de.craftmania.dockerizedcraft.connection.balancer.BalancedReconnectHandler;
4 | import de.craftmania.dockerizedcraft.connection.balancer.ConnectionBalancer;
5 | import de.craftmania.dockerizedcraft.connection.balancer.session.RedisSessionStorage;
6 | import de.craftmania.dockerizedcraft.connection.balancer.session.SessionStorage;
7 | import de.craftmania.dockerizedcraft.container.inspector.IContainerInspector;
8 | import de.craftmania.dockerizedcraft.container.inspector.docker.DockerContainerInspector;
9 | import de.craftmania.dockerizedcraft.container.inspector.kubernetes.KubernetesContainerInspector;
10 | import de.craftmania.dockerizedcraft.plugin.notifier.serverlist.ServerListPluginNotifier;
11 | import de.craftmania.dockerizedcraft.server.updater.ServerUpdater;
12 | import net.md_5.bungee.api.ReconnectHandler;
13 | import net.md_5.bungee.api.plugin.Plugin;
14 | import net.md_5.bungee.config.*;
15 | import org.bstats.bungeecord.Metrics;
16 |
17 | import java.io.IOException;
18 | import java.io.File;
19 | import java.io.InputStream;
20 | import java.nio.file.Files;
21 | import java.util.*;
22 | import java.util.concurrent.TimeUnit;
23 |
24 |
25 | @SuppressWarnings("unused")
26 | public class DockerizedCraft extends Plugin {
27 | private Map configuration;
28 |
29 | /**
30 | * Enabled sub-packages depending on the given configuration
31 | */
32 | @Override
33 | public void onEnable() {
34 | // bStats
35 | //Metrics metrics = new Metrics(this);
36 |
37 | try {
38 | this.loadConfiguration();
39 | } catch (IOException e) {
40 | getLogger().warning("Not able to write Configuration File.");
41 | getLogger().warning("Check write Permissions to the plugin directory.");
42 | getLogger().warning("Stopped Plugin enabling (Plugin will not work!)");
43 | e.printStackTrace();
44 | return;
45 | }
46 |
47 | if (getConfiguration().get("connection-balancer").getBoolean("enabled")) {
48 | getLogger().info("[Connection ConnectionBalancer] Enabled!");
49 | bootstrapConnectionBalancer(getConfiguration().get("connection-balancer"));
50 | }
51 |
52 |
53 | if (getConfiguration().get("server-updater").getBoolean("enabled")) {
54 | getLogger().info("[Server Updater] Enabled!");
55 | bootstrapServerUpdater(getConfiguration().get("server-updater"));
56 | }
57 |
58 | if (getConfiguration().get("plugin-notifier").getBoolean("enabled")) {
59 | getLogger().info("[Plugin Notification] Enabled!");
60 | bootstrapPluginNotifier(getConfiguration().get("plugin-notifier"));
61 | }
62 |
63 | if (getConfiguration().get("container-inspector").getBoolean("enabled")) {
64 | getLogger().info("[Container Inspector] Enabled!");
65 | bootstrapContainerInspector(
66 | getConfiguration().get("container-inspector")
67 | );
68 | }
69 | }
70 |
71 | /**
72 | * Bootstraps the Connection Balancer, sets the reconnect handler and adds the registered listener
73 | * @param configuration The connection balancer configuration
74 | */
75 | private void bootstrapConnectionBalancer(Configuration configuration) {
76 | ConnectionBalancer connectionBalancer = new ConnectionBalancer(
77 | configuration,
78 | getLogger(),
79 | this
80 | );
81 |
82 | SessionStorage sessionStorage = new RedisSessionStorage(
83 | configuration.getString("session-store.redis.host"),
84 | configuration.getString("session-store.redis.password"),
85 | configuration.getInt("session-store.redis.port"),
86 | configuration.getBoolean("session-store.redis.ssl")
87 | );
88 |
89 | ReconnectHandler reconnectHandler = new BalancedReconnectHandler(connectionBalancer, sessionStorage, this.getLogger());
90 | getProxy().setReconnectHandler(reconnectHandler);
91 | getProxy().getPluginManager().registerListener(this, connectionBalancer);
92 | }
93 |
94 | /**
95 | * Bootstraps the server update and adds it the the registered listeners
96 | * @param configuration The server updater configuration
97 | */
98 | private void bootstrapServerUpdater(Configuration configuration) {
99 | ServerUpdater serverUpdater = new ServerUpdater(configuration, getProxy(), getLogger());
100 | getProxy().getPluginManager().registerListener(this, serverUpdater);
101 | }
102 |
103 | /**
104 | * Bootstraps the plugin notifier and adds scheduled interval tasks
105 | * @param configuration The plugin notifier configuration
106 | */
107 | private void bootstrapPluginNotifier(Configuration configuration) {
108 | ServerListPluginNotifier notifier = new ServerListPluginNotifier(
109 | configuration.getSection("meta-data-mapper"),
110 | getLogger()
111 | );
112 |
113 | getProxy().getPluginManager().registerListener(this, notifier);
114 | getProxy().getScheduler().schedule(
115 | this,
116 | notifier::sendUpdate,
117 | 0,
118 | configuration.getInt("refresh-interval"),
119 | TimeUnit.SECONDS
120 | );
121 | }
122 |
123 | /**
124 | * Bootstraps the container inspector and runs the inspector and listener as async task through the scheduler
125 | * @param configuration The container inspector configuration
126 | */
127 | private void bootstrapContainerInspector(Configuration configuration) {
128 |
129 | IContainerInspector containerInspector;
130 | if (configuration.getString("backend").equals("docker")) {
131 | containerInspector = new DockerContainerInspector(configuration, getProxy(), getLogger());
132 | } else {
133 | containerInspector = new KubernetesContainerInspector(configuration, getProxy(), getLogger());
134 | }
135 |
136 | getProxy().getScheduler().runAsync(this, containerInspector::runContainerInspection);
137 | getProxy().getScheduler().runAsync(this, containerInspector::runContainerListener);
138 | }
139 |
140 | /**
141 | * Loads the configurations
142 | * @throws IOException On missing write access
143 | */
144 | private void loadConfiguration() throws IOException {
145 |
146 | List configNames = Arrays.asList(
147 | "connection-balancer",
148 | "plugin-notifier",
149 | "container-inspector",
150 | "server-updater"
151 | );
152 | Map configuration = new HashMap<>(configNames.size());
153 |
154 |
155 | if (!getDataFolder().exists()) {
156 | if (!getDataFolder().mkdir()) {
157 | throw new IOException("Not able to generate Plugin Data Folder");
158 | }
159 | }
160 |
161 |
162 | for (String configName : configNames) {
163 |
164 |
165 | File file = new File(getDataFolder(), configName + ".yml");
166 |
167 | if (!file.exists()) {
168 | try (InputStream in = getResourceAsStream(configName + ".yml")) {
169 | Files.copy(in, file.toPath());
170 | } catch (IOException e) {
171 | e.printStackTrace();
172 | }
173 | }
174 | configuration.put(configName, ConfigurationProvider.getProvider(YamlConfiguration.class)
175 | .load(new File(getDataFolder(), configName + ".yml")
176 | ));
177 | }
178 |
179 | this.configuration = configuration;
180 | }
181 |
182 | /**
183 | * @return Map containing the sub-package configurations
184 | */
185 | private Map getConfiguration() {
186 | return configuration;
187 | }
188 |
189 | }
190 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/BalancedReconnectHandler.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer;
2 |
3 | import com.google.common.base.Preconditions;
4 | import de.craftmania.dockerizedcraft.connection.balancer.session.SessionStorage;
5 | import net.md_5.bungee.api.ReconnectHandler;
6 | import net.md_5.bungee.api.config.ServerInfo;
7 | import net.md_5.bungee.api.connection.ProxiedPlayer;
8 | import java.util.logging.Logger;
9 |
10 | public class BalancedReconnectHandler implements ReconnectHandler {
11 | private ConnectionBalancer connectionBalancer;
12 | private SessionStorage sessionStorage;
13 | private Logger logger;
14 |
15 | public BalancedReconnectHandler(ConnectionBalancer connectionBalancer, SessionStorage sessionStorage, Logger logger) {
16 | this.connectionBalancer = connectionBalancer;
17 | this.sessionStorage = sessionStorage;
18 | this.logger = logger;
19 | }
20 |
21 | @Override
22 | public ServerInfo getServer(ProxiedPlayer proxiedPlayer) {
23 | ServerInfo serverInfo = null;
24 |
25 | logger.info("Looking for a server to match: hostname="+proxiedPlayer.getPendingConnection().getVirtualHost().getHostName()+", session="+this.sessionStorage.getReconnectServer(proxiedPlayer.getUniqueId()));
26 | if (proxiedPlayer.getPendingConnection().getVirtualHost().getHostName() != null) {
27 | serverInfo = this.connectionBalancer.getForcedServer(proxiedPlayer.getPendingConnection().getVirtualHost().getHostName());
28 | }
29 |
30 | if (serverInfo == null && this.sessionStorage.getReconnectServer(proxiedPlayer.getUniqueId()) != null) {
31 | serverInfo = this.connectionBalancer.getReconnectServer(this.sessionStorage.getReconnectServer(proxiedPlayer.getUniqueId()));
32 | }
33 |
34 | if (serverInfo == null) {
35 | serverInfo = this.connectionBalancer.getFallbackServer();
36 | }
37 |
38 | Preconditions.checkState( serverInfo != null, "Default server or group not defined" );
39 |
40 | return serverInfo;
41 | }
42 |
43 | @Override
44 | public void setServer(ProxiedPlayer proxiedPlayer) {
45 | this.sessionStorage.setReconnectServer(
46 | proxiedPlayer.getUniqueId(),
47 | ( proxiedPlayer.getReconnectServer() != null )
48 | ? proxiedPlayer.getReconnectServer().getName()
49 | : proxiedPlayer.getServer().getInfo().getName()
50 | );
51 | }
52 |
53 | @Override
54 | public void save() {
55 |
56 | }
57 |
58 | @Override
59 | public void close() {
60 | }
61 |
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/ConnectionBalancer.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer;
2 |
3 | import de.craftmania.dockerizedcraft.connection.balancer.command.GroupCommand;
4 | import de.craftmania.dockerizedcraft.connection.balancer.strategy.BalanceStrategy;
5 | import de.craftmania.dockerizedcraft.connection.balancer.strategy.Strategy;
6 | import de.craftmania.dockerizedcraft.server.updater.events.PostAddServerEvent;
7 | import de.craftmania.dockerizedcraft.server.updater.events.PreRemoveServerEvent;
8 | import de.craftmania.dockerizedcraft.connection.balancer.model.Group;
9 | import net.md_5.bungee.api.config.ServerInfo;
10 | import net.md_5.bungee.api.event.PluginMessageEvent;
11 | import net.md_5.bungee.api.plugin.Listener;
12 | import net.md_5.bungee.api.plugin.Plugin;
13 | import net.md_5.bungee.api.plugin.PluginManager;
14 | import net.md_5.bungee.config.Configuration;
15 | import net.md_5.bungee.event.EventHandler;
16 |
17 | import java.io.ByteArrayInputStream;
18 | import java.io.DataInputStream;
19 | import java.util.Collections;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 | import java.util.logging.Logger;
23 |
24 | public class ConnectionBalancer implements Listener {
25 |
26 | private static String defaultGroupName;
27 |
28 | static {
29 | defaultGroupName = "default";
30 | }
31 |
32 | private String groupEnvironmentVariable;
33 | private Group defaultGroup;
34 | private Map groups;
35 |
36 | private Map servers;
37 | private Map serverGroups;
38 |
39 | private String forcedHostEnvironmentVariable;
40 | private Map forcedHostServers;
41 | private Map forcedHostGroups;
42 |
43 | private Logger logger;
44 | private Plugin plugin;
45 |
46 | public static final Map balanceStrategies;
47 |
48 | static {
49 | Map strategies = new HashMap<>(1);
50 | strategies.put("balance", new BalanceStrategy());
51 | balanceStrategies = Collections.unmodifiableMap(strategies);
52 | }
53 |
54 | public ConnectionBalancer(
55 | Configuration configuration,
56 | Logger logger,
57 | Plugin plugin
58 | ) {
59 | this.logger = logger;
60 | this.plugin = plugin;
61 | this.servers = new HashMap<>();
62 | this.serverGroups = new HashMap<>();
63 | this.forcedHostServers = new HashMap<>();
64 |
65 | this.groupEnvironmentVariable = configuration.getString("environment-variables.group");
66 | this.forcedHostEnvironmentVariable = configuration.getString("environment-variables.forced_host");
67 |
68 | this.groups = this.getGroupsByConfiguration(configuration.getSection("groups"));
69 | this.logger.info("[Connection Balancer] Added " + this.groups.size() + " server groups.");
70 |
71 | this.defaultGroup =
72 | this.getGroup(configuration.getString("default-group")) != null
73 | ? this.getGroup(configuration.getString("default-group"))
74 | : this.getGroup(ConnectionBalancer.defaultGroupName);
75 |
76 | assert this.defaultGroup != null;
77 | this.logger.info("[Connection Balancer] Setting default group: " + this.defaultGroup.getName());
78 |
79 | this.forcedHostGroups = this.getGroupsForcedHosts(this.groups, configuration.getSection("forced-hosts"));
80 |
81 | for (Map.Entry entry : this.forcedHostGroups.entrySet()) {
82 | this.logger.info(
83 | "[Connection Balancer] Added forced host: "
84 | + entry.getKey()
85 | + " > "
86 | + entry.getValue().getName()
87 | );
88 | }
89 |
90 | this.loadCommands(configuration.getSection("join-commands"));
91 | }
92 |
93 | private void loadCommands(Configuration joinCommandsConfiguration) {
94 | for (String commandName : joinCommandsConfiguration.getKeys()) {
95 | if (this.groups.containsKey(joinCommandsConfiguration.getString(commandName))) {
96 |
97 | GroupCommand command = new GroupCommand(
98 | commandName,
99 | this.groups.get(joinCommandsConfiguration.getString(commandName)),
100 | this
101 | );
102 | this.plugin.getProxy().getPluginManager().registerCommand(this.plugin, command);
103 | this.logger.info(
104 | "[Connection Balancer] Registered Group join Command: "
105 | + commandName
106 | + " > "
107 | + joinCommandsConfiguration.getString(commandName)
108 | );
109 | }
110 | }
111 | }
112 |
113 | @SuppressWarnings("WeakerAccess")
114 | public ServerInfo getReconnectServer(String name) {
115 | if (this.servers.containsKey(name)) {
116 | if (this.groups.get(this.serverGroups.get(name)).getCanReconnect()) {
117 | return this.servers.get(name);
118 | }
119 |
120 | return null;
121 | }
122 |
123 | return null;
124 | }
125 |
126 | @SuppressWarnings("WeakerAccess")
127 | public ServerInfo getForcedServer(String hostname) {
128 | if (this.forcedHostGroups.containsKey(hostname)) {
129 | return this.forcedHostGroups.get(hostname).getStrategy().getServer(this.forcedHostGroups.get(hostname).getServers());
130 | }
131 |
132 | if (this.forcedHostServers.containsKey(hostname)) {
133 | return this.forcedHostServers.get(hostname);
134 | }
135 |
136 | return null;
137 | }
138 |
139 | @SuppressWarnings("WeakerAccess")
140 | public ServerInfo getFallbackServer() {
141 | this.logger.info("Found " + this.defaultGroup.getServers().size() + " default servers");
142 | return this.defaultGroup.getStrategy().getServer(this.defaultGroup.getServers());
143 | }
144 |
145 | private Group getGroup(String name) {
146 | if (this.groups.containsKey(name)) {
147 | return this.groups.get(name);
148 | }
149 |
150 | return null;
151 | }
152 |
153 | private Map getGroupsForcedHosts(Map groups, Configuration forcedHostConfiguration) {
154 |
155 | Map forcedHosts = new HashMap<>(forcedHostConfiguration.getKeys().size());
156 |
157 | for (String key : forcedHostConfiguration.getKeys()) {
158 |
159 | if (groups.containsKey(forcedHostConfiguration.getString(key))) {
160 | forcedHosts.put(key.replace("{dot}", "."), groups.get(forcedHostConfiguration.getString(key)));
161 | } else {
162 | this.logger.warning(
163 | "[Connection Balancer] Could not add forced host "
164 | + key.replace("{dot}", ".")
165 | + ": Group "
166 | + forcedHostConfiguration.getString(key)
167 | + " does not exists."
168 | );
169 | }
170 | }
171 |
172 | return forcedHosts;
173 | }
174 |
175 | private Map getGroupsByConfiguration(Configuration groupConfig) {
176 | // Add default group if not configured
177 | if (!groupConfig.contains(ConnectionBalancer.defaultGroupName)) {
178 |
179 | Configuration defaultGroupConfig = new Configuration();
180 | defaultGroupConfig.set("strategy", "balance");
181 | defaultGroupConfig.set("can-reconnect", true);
182 | defaultGroupConfig.set("restricted", false);
183 | groupConfig.set(ConnectionBalancer.defaultGroupName, defaultGroupConfig);
184 | }
185 |
186 | // Reset groups maps
187 | Map groups = new HashMap<>(groupConfig.getKeys().size());
188 |
189 | // Now lets add configured groups
190 | for (String key : groupConfig.getKeys()) {
191 | groups.put(key, new Group(
192 | key,
193 | groupConfig.getString(key + ".strategy"),
194 | groupConfig.getBoolean(key + ".restricted"),
195 | groupConfig.getBoolean(key + ".can_reconnect")
196 | ));
197 | }
198 |
199 | return groups;
200 | }
201 |
202 | @SuppressWarnings("WeakerAccess")
203 | public void addServer(ServerInfo server) {
204 | this.servers.put(server.getName(), server);
205 | }
206 |
207 | @SuppressWarnings("WeakerAccess")
208 | public void removeServer(ServerInfo server) {
209 | this.servers.remove(server.getName());
210 | }
211 |
212 | @SuppressWarnings("unused")
213 | public void removeServer(String serverName) {
214 | this.servers.remove(serverName);
215 | }
216 |
217 | @SuppressWarnings("unused")
218 | public ServerInfo getServer(ServerInfo server) {
219 | return this.servers.get(server.getName());
220 | }
221 |
222 | @SuppressWarnings({"WeakerAccess", "unused"})
223 | public ServerInfo getServer(String name) {
224 | return this.servers.get(name);
225 | }
226 |
227 | @SuppressWarnings({"WeakerAccess", "unused"})
228 | public String getServerGroup(String name) {
229 | return this.serverGroups.get(name);
230 | }
231 |
232 | @SuppressWarnings({"WeakerAccess", "unused"})
233 | public String getServerGroup(ServerInfo server) {
234 | return this.serverGroups.get(server.getName());
235 | }
236 |
237 | @EventHandler
238 | @SuppressWarnings("unused")
239 | public void onPostAddServer(PostAddServerEvent event) {
240 | String groupName = ConnectionBalancer.defaultGroupName;
241 | if (event.getEnvironmentVariables().containsKey(this.groupEnvironmentVariable)) {
242 | groupName = event.getEnvironmentVariables().get(this.groupEnvironmentVariable);
243 | }
244 |
245 | if (!this.groups.containsKey(groupName)) {
246 | groupName = ConnectionBalancer.defaultGroupName;
247 | }
248 |
249 | this.addServer(event.getServerInfo());
250 | this.logger.info("[Connection Balancer] Added Server: " + event.getServerInfo().getName());
251 | this.groups.get(groupName).addServer(event.getServerInfo());
252 | this.serverGroups.put(event.getServerInfo().getName(), groupName);
253 | this.logger.info("[Connection Balancer] Added Server to group: " + event.getServerInfo().getName() + " > " + groupName);
254 | if (event.getEnvironmentVariables().containsKey(this.forcedHostEnvironmentVariable)) {
255 | String forcedHost = event.getEnvironmentVariables().get(this.forcedHostEnvironmentVariable);
256 |
257 | if (this.forcedHostServers.containsKey(forcedHost)) {
258 | this.logger.warning(
259 | "[Connection Balancer] Overwriting existing Forced Host: "
260 | + forcedHost
261 | + " > "
262 | + this.forcedHostServers.get(forcedHost).getName()
263 | );
264 | }
265 |
266 | this.logger.info("[Connection Balancer] Adding Server Forced Host: "
267 | + forcedHost
268 | + " > "
269 | + event.getServerInfo().getName());
270 | this.forcedHostServers.put(
271 | forcedHost,
272 | event.getServerInfo()
273 | );
274 | }
275 | }
276 |
277 | @EventHandler
278 | @SuppressWarnings("unused")
279 | public void onPreRemoveServer(PreRemoveServerEvent event) {
280 | if (!this.servers.containsKey(event.getServerInfo().getName())) {
281 | return;
282 | }
283 | ServerInfo server = this.servers.get(event.getServerInfo().getName());
284 |
285 | this.logger.info("[Connection Balancer] Removing Server from group: " + event.getServerInfo().getName() + " < " + this.serverGroups.get(server.getName()));
286 | this.groups.get(this.serverGroups.get(server.getName())).removeServer(server);
287 | this.serverGroups.remove(server.getName());
288 | this.logger.info("[Connection Balancer] Removing Server: " + event.getServerInfo().getName());
289 | this.removeServer(server);
290 |
291 | for (String entry : this.forcedHostServers.keySet()) {
292 | if (this.forcedHostServers.get(entry).getName().equals(server.getName())) {
293 | this.logger.info("[Connection Balancer] Removing Server Forced Host: "
294 | + entry
295 | + " > "
296 | + server.getName());
297 |
298 | this.forcedHostServers.remove(entry);
299 | break;
300 | }
301 | }
302 | }
303 |
304 | @EventHandler
305 | @SuppressWarnings("unused")
306 | public void onPluginMessage(PluginMessageEvent event) {
307 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(event.getData()));
308 | try {
309 | String subchanncel = in.readUTF();
310 | this.logger.info(subchanncel);
311 | } catch (Exception ignored) {
312 | }
313 | }
314 | }
315 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/command/GroupCommand.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.command;
2 |
3 | import de.craftmania.dockerizedcraft.connection.balancer.ConnectionBalancer;
4 | import de.craftmania.dockerizedcraft.connection.balancer.model.Group;
5 | import net.md_5.bungee.api.ChatColor;
6 | import net.md_5.bungee.api.CommandSender;
7 | import net.md_5.bungee.api.chat.ComponentBuilder;
8 | import net.md_5.bungee.api.connection.ProxiedPlayer;
9 | import net.md_5.bungee.api.plugin.Command;
10 |
11 | public class GroupCommand extends Command {
12 |
13 | private Group group;
14 |
15 | private ConnectionBalancer connectionBalancer;
16 |
17 | public GroupCommand(String name, Group group, ConnectionBalancer connectionBalancer) {
18 | super(name);
19 | this.group = group;
20 | this.connectionBalancer = connectionBalancer;
21 | }
22 |
23 | @Override
24 | public void execute(CommandSender commandSender, String[] strings) {
25 | if(commandSender instanceof ProxiedPlayer){
26 | ProxiedPlayer player = (ProxiedPlayer) commandSender;
27 |
28 | String currentServer = player.getServer().getInfo().getName();
29 | if (connectionBalancer.getServerGroup(currentServer).equalsIgnoreCase(group.getName())) {
30 | player.sendMessage(new ComponentBuilder("You are already connected to this server group!").color(ChatColor.RED).create());
31 | return;
32 | }
33 |
34 | player.connect(group.getStrategy().getServer(group.getServers()));
35 |
36 | }else{
37 | commandSender.sendMessage(new ComponentBuilder("This command can only be run by a player!").color(ChatColor.RED).create());
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/model/Group.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.model;
2 |
3 | import de.craftmania.dockerizedcraft.connection.balancer.ConnectionBalancer;
4 | import de.craftmania.dockerizedcraft.connection.balancer.strategy.Strategy;
5 | import net.md_5.bungee.api.config.ServerInfo;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | public class Group {
11 | private String name;
12 |
13 | private Strategy strategy;
14 |
15 | private Boolean restricted;
16 |
17 | private Boolean canReconnect;
18 |
19 | private Map servers;
20 |
21 | public Group(String name, Strategy strategy, Boolean restricted, Boolean canReconnect) {
22 | this.name = name;
23 | this.strategy = strategy;
24 | this.restricted = restricted;
25 | this.canReconnect = canReconnect;
26 | this.servers = new HashMap<>();
27 | }
28 |
29 | public Group(String name, String strategy, Boolean restricted, Boolean canReconnect) {
30 | this.name = name;
31 | this.setStrategy(strategy);
32 | this.restricted = restricted;
33 | this.canReconnect = canReconnect;
34 | this.servers = new HashMap<>();
35 | }
36 |
37 |
38 | @SuppressWarnings({"WeakerAccess", "unused"})
39 | public String getName() {
40 | return name;
41 | }
42 |
43 | @SuppressWarnings({"WeakerAccess", "unused"})
44 | public void setName(String name) {
45 | this.name = name;
46 | }
47 |
48 | @SuppressWarnings({"WeakerAccess", "unused"})
49 | public Strategy getStrategy() {
50 | return strategy;
51 | }
52 |
53 | @SuppressWarnings({"WeakerAccess", "unused"})
54 | public void setStrategy(Strategy strategy) {
55 | this.strategy = strategy;
56 | }
57 |
58 | @SuppressWarnings({"WeakerAccess", "unused"})
59 | public void setStrategy(String strategy) {
60 | if (ConnectionBalancer.balanceStrategies.containsKey(strategy)) {
61 | this.strategy = ConnectionBalancer.balanceStrategies.get(strategy);
62 | } else {
63 | this.strategy = ConnectionBalancer.balanceStrategies.get("balance");
64 | }
65 | }
66 |
67 | @SuppressWarnings({"WeakerAccess", "unused"})
68 | public Boolean getRestricted() {
69 | return restricted;
70 | }
71 |
72 | @SuppressWarnings({"WeakerAccess", "unused"})
73 | public void setRestricted(Boolean restricted) {
74 | this.restricted = restricted;
75 | }
76 |
77 | @SuppressWarnings({"WeakerAccess", "unused"})
78 | public Boolean getCanReconnect() {
79 | return canReconnect;
80 | }
81 |
82 | @SuppressWarnings({"WeakerAccess", "unused"})
83 | public void setCanReconnect(Boolean canReconnect) {
84 | this.canReconnect = canReconnect;
85 | }
86 |
87 | @Override
88 | public String toString() {
89 | return super.toString() + "{" + this.name + "," + this.strategy + "}";
90 | }
91 |
92 | @SuppressWarnings({"WeakerAccess", "unused"})
93 | public Map getServers() {
94 | return this.servers;
95 | }
96 |
97 | @SuppressWarnings({"WeakerAccess", "unused"})
98 | public void addServer(ServerInfo server) {
99 | this.servers.put(server.getName(), server);
100 | }
101 |
102 | @SuppressWarnings({"WeakerAccess", "unused"})
103 | public ServerInfo getServer(String name) {
104 | return this.servers.get(name);
105 | }
106 |
107 |
108 | @SuppressWarnings({"WeakerAccess", "unused"})
109 | public void removeServer(ServerInfo server) {
110 | this.servers.remove(server.getName());
111 | }
112 |
113 |
114 | @SuppressWarnings({"WeakerAccess", "unused"})
115 | public void removeServer(String server) {
116 | this.servers.remove(server);
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/session/RedisSessionStorage.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.session;
2 |
3 | import redis.clients.jedis.Jedis;
4 | import redis.clients.jedis.JedisPool;
5 | import redis.clients.jedis.exceptions.JedisConnectionException;
6 |
7 | import java.util.UUID;
8 |
9 | public class RedisSessionStorage implements SessionStorage {
10 |
11 | static private String prefix;
12 |
13 | static {
14 | prefix = "player/session/";
15 | }
16 |
17 | private JedisPool jedisPool;
18 | private String password;
19 |
20 | public RedisSessionStorage(String hostname, String password, Integer port, Boolean ssl) {
21 | this.jedisPool = new JedisPool(hostname, port);
22 | this.password = password;
23 | }
24 |
25 | @Override
26 | public void setReconnectServer(UUID uuid, String serverName) {
27 | try (Jedis jedis = this.jedisPool.getResource()) {
28 | if (this.password != null && !this.password.equals("")) {
29 | jedis.auth(this.password);
30 | }
31 | jedis.set((RedisSessionStorage.prefix + uuid.toString()), serverName);
32 | }
33 | }
34 |
35 | @Override
36 | public String getReconnectServer(UUID uuid) {
37 | try (Jedis jedis = this.jedisPool.getResource()) {
38 | if (this.password != null && !this.password.equals("")) {
39 | jedis.auth(this.password);
40 | }
41 | System.out.print(jedis.get((RedisSessionStorage.prefix + uuid.toString())));
42 | return jedis.get((RedisSessionStorage.prefix + uuid.toString()));
43 | } catch (Exception e) {
44 | return null;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/session/SessionStorage.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.session;
2 |
3 | import java.util.UUID;
4 |
5 | public interface SessionStorage {
6 | void setReconnectServer(UUID uuid, String serverName);
7 |
8 | String getReconnectServer(UUID uuid);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/strategy/BalanceStrategy.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.strategy;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 |
5 | import java.util.Map;
6 |
7 | public class BalanceStrategy implements Strategy {
8 | @Override
9 | public ServerInfo getServer(Map server) {
10 | ServerInfo selectedServer = null;
11 |
12 | for (String key: server.keySet()) {
13 | if (selectedServer == null) {
14 | selectedServer = server.get(key);
15 | } else if (selectedServer.getPlayers().size() > server.get(key).getPlayers().size()) {
16 | selectedServer = server.get(key);
17 | }
18 | }
19 |
20 | return selectedServer;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/connection/balancer/strategy/Strategy.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.connection.balancer.strategy;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 |
5 | import java.util.Map;
6 |
7 | public interface Strategy {
8 | ServerInfo getServer(Map server);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/IContainerInspector.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector;
2 |
3 | public interface IContainerInspector {
4 | void runContainerInspection();
5 | void runContainerListener();
6 | }
7 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/docker/DockerClientFactory.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.docker;
2 |
3 | import com.github.dockerjava.api.DockerClient;
4 | import com.github.dockerjava.core.DefaultDockerClientConfig;
5 | import com.github.dockerjava.core.DockerClientBuilder;
6 | import com.github.dockerjava.core.DockerClientConfig;
7 | import net.md_5.bungee.config.Configuration;
8 |
9 | class DockerClientFactory {
10 |
11 | static DockerClient getByConfiguration(Configuration configuration) {
12 | return DockerClientFactory.get(
13 | configuration.getString("docker.host"),
14 | configuration.getBoolean("docker.tsl-verify"),
15 | configuration.getString("docker.cert-path"),
16 | configuration.getString("docker.registry.username"),
17 | configuration.getString("docker.registry.password"),
18 | configuration.getString("docker.registry.email"),
19 | configuration.getString("docker.registry.url")
20 | );
21 | }
22 |
23 | private static DockerClient get(
24 | String host,
25 | Boolean tlsVerify,
26 | String certPath,
27 | String registryUsername,
28 | String registryPass,
29 | String registryMail,
30 | String registryUrl
31 | ) {
32 | DefaultDockerClientConfig.Builder configBuilder = DefaultDockerClientConfig.createDefaultConfigBuilder()
33 | .withDockerHost(host);
34 |
35 | configBuilder.withDockerTlsVerify(tlsVerify);
36 |
37 | if (!certPath.equals("")) {
38 | configBuilder.withDockerCertPath(certPath);
39 | }
40 |
41 | if(!registryUrl.equals("")) {
42 | configBuilder.withRegistryUrl(registryUrl);
43 | if (!registryUsername.equals("")) {
44 | configBuilder.withRegistryUsername(registryUsername);
45 | }
46 |
47 | if (!registryMail.equals("")) {
48 | configBuilder.withRegistryEmail(registryMail);
49 | }
50 |
51 | if (!registryPass.equals("")) {
52 | configBuilder.withRegistryPassword(registryPass);
53 | }
54 | }
55 |
56 | DockerClientConfig config = configBuilder.build();
57 |
58 | return DockerClientBuilder.getInstance(config).build();
59 | }
60 | }
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/docker/DockerContainerInspector.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.docker;
2 |
3 | import com.github.dockerjava.api.DockerClient;
4 | import com.github.dockerjava.api.model.Container;
5 | import com.github.dockerjava.api.model.Event;
6 | import com.github.dockerjava.api.model.EventType;
7 | import com.github.dockerjava.core.command.EventsResultCallback;
8 | import de.craftmania.dockerizedcraft.container.inspector.IContainerInspector;
9 | import net.md_5.bungee.api.ProxyServer;
10 | import net.md_5.bungee.config.Configuration;
11 |
12 | import java.io.IOException;
13 | import java.util.List;
14 | import java.util.logging.Logger;
15 |
16 | public class DockerContainerInspector implements IContainerInspector {
17 | private DockerClient dockerClient;
18 |
19 | private ProxyServer proxyServer;
20 |
21 | private String network;
22 |
23 | private Logger logger;
24 |
25 | public DockerContainerInspector(Configuration configuration, ProxyServer proxyServer, Logger logger) {
26 | this.proxyServer = proxyServer;
27 | this.network = configuration.getString("docker.network");
28 | this.logger = logger;
29 | this.dockerClient = DockerClientFactory.getByConfiguration(configuration);
30 | }
31 |
32 | public void runContainerInspection() {
33 | this.logger.info("[Docker Container Inspector] Running initial inspection.");
34 |
35 | EventsResultCallback callback = this.getEventResultCallback();
36 | List containers = this.dockerClient.listContainersCmd().exec();
37 |
38 | // Trigger fake Event to use same Result Callback
39 | for (Container container: containers) {
40 | Event event = new Event("start", container.getId(), container.getImage(), System.currentTimeMillis())
41 | .withAction("bootstrap")
42 | .withType(EventType.forValue("container"));
43 |
44 | callback.onNext(event);
45 | }
46 | }
47 |
48 | public void runContainerListener() {
49 | this.logger.info("[Docker Container Inspector] Running listener.");
50 | try {
51 | this.dockerClient.eventsCmd().exec(this.getEventResultCallback()).awaitCompletion().close();
52 | } catch (IOException |InterruptedException e) {
53 | e.printStackTrace();
54 | }
55 | }
56 |
57 | private EventsResultCallback getEventResultCallback() {
58 | return new ResultCallback(
59 | this.dockerClient,
60 | this.proxyServer,
61 | this.network
62 | );
63 | }
64 | }
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/docker/ResultCallback.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.docker;
2 |
3 | import com.github.dockerjava.api.DockerClient;
4 | import com.github.dockerjava.api.command.InspectContainerResponse;
5 | import com.github.dockerjava.api.exception.NotFoundException;
6 | import com.github.dockerjava.api.model.Event;
7 | import com.github.dockerjava.api.model.ExposedPort;
8 | import com.github.dockerjava.api.model.Ports;
9 | import com.github.dockerjava.core.command.EventsResultCallback;
10 | import de.craftmania.dockerizedcraft.container.inspector.events.ContainerEvent;
11 | import net.md_5.bungee.api.ProxyServer;
12 |
13 | import java.net.InetAddress;
14 | import java.net.UnknownHostException;
15 | import java.util.*;
16 |
17 | public class ResultCallback extends EventsResultCallback {
18 | private DockerClient dockerClient;
19 | private ProxyServer proxyServer;
20 | private String network;
21 |
22 | ResultCallback(DockerClient dockerClient, ProxyServer proxyServer, String network) {
23 | this.dockerClient = dockerClient;
24 | this.proxyServer = proxyServer;
25 | this.network = network;
26 | }
27 |
28 | @Override
29 | public void onNext(Event event) {
30 | if (event.getType() == null || !event.getType().getValue().equals("container")) {
31 | super.onNext(event);
32 | return;
33 | }
34 |
35 | ContainerEvent containerEvent = new ContainerEvent(event.getId(), event.getAction());
36 |
37 | // Not lets inspect the container, we won't fire any events without network information
38 | try {
39 | InspectContainerResponse info = this.dockerClient.inspectContainerCmd(event.getId()).exec();
40 |
41 | containerEvent.setName(info.getName());
42 | containerEvent.setEnvironmentVariables(this.getEnvironmentVariables(info));
43 | containerEvent.setPort(this.getPort(info));
44 | containerEvent.setIp(this.getIp(info, this.network));
45 |
46 | } catch (NotFoundException e) {
47 | super.onNext(event);
48 | return;
49 | }
50 |
51 | super.onNext(event);
52 | this.proxyServer.getPluginManager().callEvent(containerEvent);
53 |
54 | }
55 |
56 |
57 | private Integer getPort(InspectContainerResponse info) {
58 | Map portBindings = info.getNetworkSettings().getPorts().getBindings();
59 |
60 | if (portBindings.keySet().size() > 0 && portBindings.keySet().iterator().next().getPort() != 0) {
61 | return portBindings.keySet().iterator().next().getPort();
62 | }
63 |
64 | return null;
65 | }
66 |
67 | private InetAddress getIp(InspectContainerResponse info, String network) {
68 | if (!info.getNetworkSettings().getNetworks().containsKey(network)) {
69 | return null;
70 | }
71 |
72 | try {
73 | return InetAddress.getByName(info.getNetworkSettings().getNetworks().get(network).getIpAddress());
74 | } catch (UnknownHostException e) {
75 | return null;
76 | }
77 | }
78 |
79 | private Map getEnvironmentVariables(InspectContainerResponse info) {
80 | String[] unformattedArray = info.getConfig().getEnv();
81 |
82 | if(unformattedArray == null || unformattedArray.length == 0) {
83 | return new HashMap<>(0);
84 | }
85 |
86 | Map formattedMap = new HashMap<>(unformattedArray.length);
87 |
88 | for (String environmentVariable: unformattedArray) {
89 | String[] parts = environmentVariable.split("=");
90 | if (parts.length == 2) {
91 | formattedMap.put(parts[0], parts[1]);
92 | }
93 | }
94 | return formattedMap;
95 | }
96 | }
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/events/ContainerEvent.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.events;
2 |
3 | import net.md_5.bungee.api.plugin.Event;
4 |
5 | import java.net.InetAddress;
6 | import java.util.Map;
7 |
8 | public class ContainerEvent extends Event {
9 | private String id;
10 |
11 | private String action;
12 |
13 | private InetAddress ip;
14 |
15 | private Integer port;
16 |
17 | private String name;
18 |
19 | private Map environmentVariables;
20 |
21 | public ContainerEvent(String id, String action) {
22 | this.id = id;
23 | this.action = action;
24 | }
25 |
26 | public String getId() {
27 | return id;
28 | }
29 |
30 | public String getAction() {
31 | return action;
32 | }
33 |
34 | public InetAddress getIp() {
35 | return ip;
36 | }
37 |
38 | public void setIp(InetAddress ip) {
39 | this.ip = ip;
40 | }
41 |
42 | public Integer getPort() {
43 | return port;
44 | }
45 |
46 | public void setPort(Integer port) {
47 | this.port = port;
48 | }
49 |
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | public void setName(String name) {
55 | this.name = name;
56 | }
57 |
58 | public Map getEnvironmentVariables() {
59 | return environmentVariables;
60 | }
61 |
62 | public void setEnvironmentVariables(Map environmentVariables) {
63 | this.environmentVariables = environmentVariables;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/kubernetes/KubernetesContainerInspector.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.kubernetes;
2 | import de.craftmania.dockerizedcraft.container.inspector.IContainerInspector;
3 | import io.fabric8.kubernetes.client.Config;
4 | import io.fabric8.kubernetes.client.DefaultKubernetesClient;
5 | import io.fabric8.kubernetes.client.KubernetesClient;
6 |
7 | import net.md_5.bungee.api.ProxyServer;
8 | import net.md_5.bungee.config.Configuration;
9 |
10 | import java.util.logging.Logger;
11 |
12 | public class KubernetesContainerInspector implements IContainerInspector {
13 | private ProxyServer proxyServer;
14 | private Logger logger;
15 | private Configuration configuration;
16 | private KubernetesClient client;
17 |
18 | public KubernetesContainerInspector(Configuration configuration, ProxyServer proxyServer, Logger logger) {
19 | this.proxyServer = proxyServer;
20 | this.logger = logger;
21 | this.configuration = configuration;
22 | this.client = new DefaultKubernetesClient();
23 | }
24 |
25 | public void runContainerInspection() {
26 | this.logger.info("[Kubernetes Container Inspector] Connecting to kubernetes.");
27 | }
28 |
29 | public void runContainerListener() {
30 | this.logger.info("[Kubernetes Container Inspector] Running listener.");
31 | String namespace = configuration.getString("kubernetes.namespace");
32 | if(namespace == null ||namespace.isEmpty()) this.logger.severe("kubernetes.namespace not set.");
33 | this.client.pods().inNamespace(namespace).watch(new PodWatcher(proxyServer, logger));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/container/inspector/kubernetes/PodWatcher.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.container.inspector.kubernetes;
2 | import net.md_5.bungee.api.ProxyServer;
3 | import io.fabric8.kubernetes.api.model.Pod;
4 | import io.fabric8.kubernetes.client.Watcher;
5 | import de.craftmania.dockerizedcraft.container.inspector.events.ContainerEvent;
6 | import io.fabric8.kubernetes.client.KubernetesClientException;
7 | import io.fabric8.kubernetes.api.model.EnvVar;
8 | import java.util.logging.Logger;
9 | import java.net.InetAddress;
10 | import java.util.*;
11 |
12 | public class PodWatcher implements Watcher {
13 |
14 | private ProxyServer proxyServer;
15 | private Logger logger;
16 | PodWatcher(ProxyServer proxyServer, Logger logger) {
17 | this.proxyServer = proxyServer;
18 | this.logger = logger;
19 | }
20 |
21 | @Override
22 | public void eventReceived(Action action, Pod resource) {
23 | try {
24 | logger.info("action: "+action);
25 | logger.info("phase: " +resource.getStatus().getPhase());
26 | Map labels = resource.getMetadata().getLabels();
27 | logger.info("labels: "+labels.toString());
28 | if(!labels.containsKey("dockerizedcraft/enabled") || !labels.get("dockerizedcraft/enabled").equals("true")) return;
29 |
30 | String dockerAction = "stop";
31 | if(resource.getStatus().getPhase().equals("Running")){
32 | dockerAction = "start";
33 | }
34 |
35 | ContainerEvent containerEvent = new ContainerEvent(resource.getMetadata().getName(), dockerAction);
36 | containerEvent.setName(resource.getMetadata().getName());
37 | logger.info("name: " +resource.getMetadata().getName());
38 | Map environmentVariables = new HashMap<>();
39 | for (EnvVar i : resource.getSpec().getContainers().get(0).getEnv()) environmentVariables.put(i.getName(),i.getValue());
40 | containerEvent.setEnvironmentVariables(environmentVariables);
41 | logger.info("env:" + environmentVariables);
42 | containerEvent.setPort(Integer.parseInt(environmentVariables.get("SERVER_PORT")));
43 | logger.info("port: "+environmentVariables.get("SERVER_PORT"));
44 | containerEvent.setIp(InetAddress.getByName(resource.getStatus().getPodIP()));
45 | logger.info("ip: "+resource.getStatus().getPodIP());
46 | this.proxyServer.getPluginManager().callEvent(containerEvent);
47 | }catch(java.net.UnknownHostException ex){
48 | logger.severe(ex.getMessage());
49 | }
50 |
51 | }
52 |
53 | @Override
54 | public void onClose(KubernetesClientException cause) {
55 | logger.warning("Watcher close due to " + cause);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/plugin/notifier/AbstractNotifier.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.plugin.notifier;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 |
5 | import java.io.ByteArrayOutputStream;
6 | import java.io.DataOutputStream;
7 | import java.io.IOException;
8 |
9 | public abstract class AbstractNotifier {
10 | @SuppressWarnings("SameParameterValue")
11 | protected void sendMessage(ServerInfo serverInfo, String channel, String subchannel, String message) {
12 | ByteArrayOutputStream stream = new ByteArrayOutputStream();
13 | DataOutputStream out = new DataOutputStream(stream);
14 | try {
15 | out.writeUTF(subchannel);
16 | out.writeUTF(message);
17 | } catch (IOException e) {
18 | e.printStackTrace();
19 | }
20 |
21 | serverInfo.sendData(channel, stream.toByteArray());
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/plugin/notifier/serverlist/ServerListPluginNotifier.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.plugin.notifier.serverlist;
2 |
3 |
4 | import com.google.gson.JsonObject;
5 | import de.craftmania.dockerizedcraft.plugin.notifier.AbstractNotifier;
6 | import de.craftmania.dockerizedcraft.server.updater.events.PostAddServerEvent;
7 | import de.craftmania.dockerizedcraft.server.updater.events.PreRemoveServerEvent;
8 | import net.md_5.bungee.api.config.ServerInfo;
9 | import net.md_5.bungee.api.plugin.Listener;
10 | import net.md_5.bungee.config.Configuration;
11 | import net.md_5.bungee.event.EventHandler;
12 |
13 | import java.util.HashMap;
14 | import java.util.Map;
15 | import java.util.logging.Logger;
16 |
17 | public class ServerListPluginNotifier extends AbstractNotifier implements Listener {
18 | private static String channel;
19 | static {
20 | channel = "DockerizedCraft";
21 | }
22 |
23 | private Configuration mappingConfig;
24 | private Logger logger;
25 |
26 | private JsonObject serverInfos;
27 | private Map servers;
28 |
29 | public ServerListPluginNotifier(Configuration mappingConfig, Logger logger) {
30 | this.logger = logger;
31 | this.mappingConfig = mappingConfig;
32 | this.serverInfos = new JsonObject();
33 | this.servers = new HashMap<>();
34 | }
35 |
36 | public void sendUpdate() {
37 | this.sendServerList(this.servers);
38 | }
39 |
40 | private void sendServerList(Map servers) {
41 | this.logger.info("[Plugin Notification] Sending update to servers");
42 | for (String serverName: servers.keySet()) {
43 | this.sendMessage(
44 | this.servers.get(serverName),
45 | ServerListPluginNotifier.channel,
46 | "ServerData",
47 | this.serverInfos.toString()
48 | );
49 | }
50 |
51 | }
52 |
53 | private JsonObject getMetaData(ServerInfo server, Map environmentVariables) {
54 | JsonObject serverMetaData = new JsonObject();
55 | serverMetaData.addProperty("address", server.getAddress().getAddress().getHostAddress() + ':' + server.getAddress().getPort());
56 | serverMetaData.addProperty("host", server.getAddress().getAddress().getHostAddress());
57 | serverMetaData.addProperty("port", server.getAddress().getPort());
58 | serverMetaData.addProperty("motd", server.getMotd());
59 | serverMetaData.addProperty("name", server.getName());
60 | serverMetaData.addProperty("proxied_players", server.getPlayers().size());
61 |
62 | for (String configKey: this.mappingConfig.getKeys()) {
63 | Configuration metaConfig = this.mappingConfig.getSection(configKey);
64 | String value = null;
65 |
66 | if (environmentVariables.containsKey(metaConfig.getString("environment-variable"))) {
67 | value = environmentVariables.get(metaConfig.getString("environment-variable"));
68 | } else if (metaConfig.contains("default")) {
69 | value = metaConfig.getString("default");
70 | }
71 |
72 | if (value == null) {
73 | if (metaConfig.getBoolean("required")) {
74 | serverMetaData.addProperty(configKey, (String) null);
75 | }
76 | } else {
77 | serverMetaData.addProperty(configKey, value);
78 | }
79 | }
80 |
81 | return serverMetaData;
82 | }
83 |
84 | @EventHandler
85 | @SuppressWarnings("unused")
86 | public void onPostAddServer(PostAddServerEvent event) {
87 | this.serverInfos.add(event.getServerInfo().getName(), this.getMetaData(event.getServerInfo(), event.getEnvironmentVariables()));
88 | this.servers.put(event.getServerInfo().getName(), event.getServerInfo());
89 |
90 | this.logger.info("[Plugin Notification] Added Server meta data: " + event.getServerInfo().getName());
91 | this.sendServerList(this.servers);
92 |
93 | }
94 |
95 | @EventHandler
96 | @SuppressWarnings("unused")
97 | public void onPreRemoveServer(PreRemoveServerEvent event) {
98 | this.serverInfos.remove(event.getServerInfo().getName());
99 | this.servers.remove(event.getServerInfo().getName());
100 |
101 | this.logger.info("[Plugin Notification] Removed Server meta data: " + event.getServerInfo().getName());
102 | this.sendServerList(this.servers);
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/server/updater/ServerUpdater.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.server.updater;
2 |
3 | import de.craftmania.dockerizedcraft.container.inspector.events.ContainerEvent;
4 | import de.craftmania.dockerizedcraft.server.updater.events.PostAddServerEvent;
5 | import de.craftmania.dockerizedcraft.server.updater.events.PostRemoveServerEvent;
6 | import de.craftmania.dockerizedcraft.server.updater.events.PreAddServerEvent;
7 | import de.craftmania.dockerizedcraft.server.updater.events.PreRemoveServerEvent;
8 | import net.md_5.bungee.api.ProxyServer;
9 | import net.md_5.bungee.api.config.ServerInfo;
10 | import net.md_5.bungee.api.plugin.Listener;
11 | import net.md_5.bungee.config.Configuration;
12 | import net.md_5.bungee.event.EventHandler;
13 |
14 | import java.net.InetSocketAddress;
15 | import java.util.List;
16 | import java.util.logging.Logger;
17 |
18 | public class ServerUpdater implements Listener {
19 |
20 | private String identifierKey;
21 |
22 | private String nameKey;
23 |
24 | private String portKey;
25 |
26 | private String motdKey;
27 |
28 | private String restrictedKey;
29 |
30 | private List addActionList;
31 |
32 | private List removeActionList;
33 |
34 | private Boolean debug;
35 |
36 | private ProxyServer proxyServer;
37 |
38 | private Logger logger;
39 |
40 | public ServerUpdater(Configuration configuration, ProxyServer proxyServer, Logger logger) {
41 | this.identifierKey = configuration.getString("environment-variables.identifier");
42 | this.nameKey = configuration.getString("environment-variables.name");
43 | this.portKey = configuration.getString("environment-variables.port");
44 | this.motdKey = configuration.getString("environment-variables.motd");
45 | this.restrictedKey = configuration.getString("environment-variables.restricted");
46 | this.addActionList = configuration.getStringList("add-actions");
47 | this.removeActionList = configuration.getStringList("remove-actions");
48 | this.debug = configuration.getBoolean("debug");
49 | this.proxyServer = proxyServer;
50 | this.logger = logger;
51 | }
52 |
53 | @EventHandler
54 | @SuppressWarnings("unused")
55 | public void onDockerEvent(ContainerEvent event) {
56 | if (!event.getEnvironmentVariables().containsKey(this.identifierKey)) {
57 | logger.info("missing identifier" + this.identifierKey);
58 | return;
59 | }
60 |
61 | if (this.addActionList.contains(event.getAction())) {
62 | this.addServer(event);
63 | }
64 |
65 | else if(this.removeActionList.contains(event.getAction())) {
66 | this.removeServer(event);
67 | }
68 | else
69 | logger.info("unknown action on event: "+event.getAction());
70 | }
71 |
72 |
73 | private void addServer(ContainerEvent eventData) {
74 |
75 | ServerInfo serverInfo = this.getServerInfoForEvent(eventData);
76 |
77 | if (serverInfo.getAddress().getHostName() == null) {
78 | this.logger.warning("[Server Updater] Could not add server:" + serverInfo.getName());
79 | this.logger.warning("[Server Updater] > Reason: No IP, is you network fine?");
80 | this.logger.warning("[Server Updater] > Trigger-Event-Action: " + eventData.getAction());
81 |
82 | return;
83 | }
84 |
85 | if (this.proxyServer.getServers().containsKey(serverInfo.getName())) {
86 | if (this.debug) {
87 | this.logger.warning("[Server Updater] Server with id " + serverInfo.getName() + " already exists in Bungeecord Proxy.");
88 | }
89 |
90 | InetSocketAddress currentAddress = this.proxyServer.getServers().get(serverInfo.getName()).getAddress();
91 |
92 | if (!currentAddress.equals(serverInfo.getAddress())) {
93 | if (this.debug) {
94 | this.logger.warning("[Server Updater] > Server address of " + serverInfo.getName() + "changed!");
95 | this.logger.warning("[Server Updater] >> Current: " + currentAddress.toString());
96 | this.logger.warning("[Server Updater] >> New: " + serverInfo.getAddress().toString());
97 | this.logger.warning("[Server Updater] >> Server removed from proxy to re-add it");
98 | }
99 | this.proxyServer.getServers().remove(serverInfo.getName());
100 | } else {
101 | if (this.debug) {
102 | this.logger.warning("[Server Updater] > Skipped!");
103 | this.logger.warning("[Server Updater] > Trigger-Event-Action: " + eventData.getAction());
104 | }
105 | return;
106 | }
107 | }
108 |
109 |
110 | this.proxyServer.getPluginManager().callEvent(new PreAddServerEvent(
111 | serverInfo,
112 | eventData.getEnvironmentVariables()
113 | ));
114 |
115 | this.proxyServer.getServers().put(serverInfo.getName(), serverInfo);
116 | this.logger.info("[Server Updater] Added server: " + serverInfo.getName());
117 | this.logger.info("[Server Updater] > Address: " + serverInfo.getAddress().toString());
118 | this.logger.info("[Server Updater] > MOTD: " + serverInfo.getMotd());
119 | this.logger.info("[Server Updater] > Trigger-Event-Action: " + eventData.getAction());
120 |
121 | this.proxyServer.getPluginManager().callEvent(new PostAddServerEvent(
122 | serverInfo,
123 | eventData.getEnvironmentVariables()
124 | ));
125 | }
126 |
127 | private void removeServer (ContainerEvent eventData) {
128 | // server id
129 | String id = this.getServerId(eventData);
130 |
131 | if (!this.proxyServer.getServers().containsKey(id)) {
132 | if(this.debug) {
133 | this.logger.warning("[Server Updater] Could not remove server: " + id);
134 | this.logger.warning("[Server Updater] > Reason: Not exists");
135 | this.logger.warning("[Server Updater] > Trigger-Event-Action: " + eventData.getAction());
136 | }
137 |
138 | return;
139 | }
140 |
141 | this.proxyServer.getPluginManager().callEvent(new PreRemoveServerEvent(
142 | this.getServerInfoForEvent(eventData),
143 | eventData.getEnvironmentVariables()
144 | ));
145 |
146 | this.proxyServer.getServers().remove(id);
147 | this.logger.info("[Server Updater] Removing Server: " + id);
148 | this.logger.info("[Server Updater] > Trigger-Event-Action: " + eventData.getAction());
149 |
150 | this.proxyServer.getPluginManager().callEvent(new PostRemoveServerEvent(id));
151 | }
152 |
153 | private ServerInfo getServerInfoForEvent(ContainerEvent eventData) {
154 | // server id
155 | String id = this.getServerId(eventData);
156 |
157 | // Getting the address to create
158 | int port = eventData.getEnvironmentVariables().get(this.portKey) != null
159 | ? Integer.parseInt(eventData.getEnvironmentVariables().get(this.portKey))
160 | : (eventData.getPort() != null ? eventData.getPort() : 25565);
161 |
162 | InetSocketAddress inetSocketAddress = new InetSocketAddress(eventData.getIp(), port);
163 |
164 |
165 | // Getting the motd
166 | String motd = eventData.getEnvironmentVariables().get(this.motdKey) != null
167 | ? eventData.getEnvironmentVariables().get(this.motdKey)
168 | : "A Minecraft Server Instance";
169 |
170 | // Getting restricted bool
171 | boolean restricted =
172 | eventData.getEnvironmentVariables().get(this.restrictedKey) != null &&
173 | eventData.getEnvironmentVariables().get(this.restrictedKey).equals("restricted");
174 |
175 | return ProxyServer.getInstance().constructServerInfo(
176 | id,
177 | inetSocketAddress,
178 | motd,
179 | restricted
180 | );
181 | }
182 |
183 | private String getServerId(ContainerEvent eventData) {
184 | if (eventData.getEnvironmentVariables().get(this.nameKey) != null) {
185 | return eventData.getEnvironmentVariables().get(this.nameKey);
186 | }
187 |
188 | if (eventData.getName() != null) {
189 | return eventData.getName().replace("/", "");
190 | }
191 |
192 | return eventData.getId();
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/server/updater/events/PostAddServerEvent.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.server.updater.events;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 | import net.md_5.bungee.api.plugin.Event;
5 |
6 | import java.util.Map;
7 |
8 | public class PostAddServerEvent extends Event {
9 | private ServerInfo serverInfo;
10 | private Map environmentVariables;
11 |
12 | public PostAddServerEvent(ServerInfo serverInfo, Map environmentVariables) {
13 | this.serverInfo = serverInfo;
14 | this.environmentVariables = environmentVariables;
15 | }
16 |
17 | @SuppressWarnings("unused")
18 | public ServerInfo getServerInfo() {
19 | return serverInfo;
20 | }
21 |
22 | @SuppressWarnings("unused")
23 | public Map getEnvironmentVariables() {
24 | return environmentVariables;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/server/updater/events/PostRemoveServerEvent.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.server.updater.events;
2 |
3 | import net.md_5.bungee.api.plugin.Event;
4 |
5 | public class PostRemoveServerEvent extends Event {
6 |
7 | private String name;
8 |
9 | @SuppressWarnings("unused")
10 | public PostRemoveServerEvent(String name) {
11 | this.name = name;
12 | }
13 |
14 | @SuppressWarnings("unused")
15 | public String getName() {
16 | return name;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/server/updater/events/PreAddServerEvent.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.server.updater.events;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 | import net.md_5.bungee.api.plugin.Event;
5 |
6 | import java.util.Map;
7 |
8 | public class PreAddServerEvent extends Event {
9 | private ServerInfo serverInfo;
10 | private Map environmentVariables;
11 |
12 | public PreAddServerEvent(ServerInfo serverInfo, Map environmentVariables) {
13 | this.serverInfo = serverInfo;
14 | this.environmentVariables = environmentVariables;
15 | }
16 |
17 | @SuppressWarnings("unused")
18 | public ServerInfo getServerInfo() {
19 | return serverInfo;
20 | }
21 |
22 | @SuppressWarnings("unused")
23 | public Map getEnvironmentVariables() {
24 | return environmentVariables;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/de/craftmania/dockerizedcraft/server/updater/events/PreRemoveServerEvent.java:
--------------------------------------------------------------------------------
1 | package de.craftmania.dockerizedcraft.server.updater.events;
2 |
3 | import net.md_5.bungee.api.config.ServerInfo;
4 | import net.md_5.bungee.api.plugin.Event;
5 |
6 | import java.util.Map;
7 |
8 | public class PreRemoveServerEvent extends Event {
9 | private ServerInfo serverInfo;
10 | private Map environmentVariables;
11 |
12 | public PreRemoveServerEvent(ServerInfo serverInfo, Map environmentVariables) {
13 | this.serverInfo = serverInfo;
14 | this.environmentVariables = environmentVariables;
15 | }
16 |
17 | @SuppressWarnings("unused")
18 | public ServerInfo getServerInfo() {
19 | return serverInfo;
20 | }
21 |
22 | @SuppressWarnings("unused")
23 | public Map getEnvironmentVariables() {
24 | return environmentVariables;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/resources/bungee.yml:
--------------------------------------------------------------------------------
1 | name: DockerizedCraft
2 | main: de.craftmania.dockerizedcraft.DockerizedCraft
3 | version: 0.2.2
4 | author: Muehre
--------------------------------------------------------------------------------
/src/main/resources/connection-balancer.yml:
--------------------------------------------------------------------------------
1 | # Care if you disable it you will need to configure default, priority and fallback servers by hand
2 | # Or use an different connection balancer handler plugin (Is it compatible?)
3 | # Type: Boolean
4 | enabled: true
5 |
6 | # Outputs extra information
7 | # Type: Boolean
8 | debug: true
9 |
10 | # To store player session for the reconnect handler
11 | # Type: Section
12 | session-store:
13 | # Type: Section
14 | redis:
15 | # Type: String
16 | host: "redis"
17 | # Type: String
18 | password: ~
19 | # Type: Integer
20 | port: 6379
21 | # Type: Boolean
22 | ssl: true
23 |
24 | # Environment variables of the containers
25 | # Type: Section
26 | environment-variables:
27 | # If the environment variable is set the server will be added to the priority list of connectionbalancer
28 | # This plugin implemented a custom load balancer which will use defined groups
29 | # Leaving the env variable blank will add the server to the default group
30 | # Type: String
31 | group: SERVER_GROUP
32 |
33 | # To enable forced host for the single instance.
34 | # You can also configure a forced host for a whole group. See groups config. I would recommend to do so events with single instances
35 | # i.e. docker run -e SERVER_FORCED_HOST=muehre.craftmania.de playerworld:latest
36 | # Type: String
37 | forced-host: SERVER_FORCED_HOST
38 |
39 | # Type: Section
40 | # check docker.event_listener.environment_variables.group_key
41 | groups:
42 | # Server group configuration
43 | # default group is used if a a container does not have a group environment variable. You can also configure it here
44 | # Type: Section
45 | # Group:
46 | # - strategy(String): balance is the only strategy atm. I will implement more as soon as they are required
47 | # - forced_host(String) Optional: people joining over this host will be send to this group
48 | # - can-reconnect(Boolean) Default: false: if a player can reconnect to this group. Usefull to disable for i.e minigames
49 | # - restricted(Boolean) Default: false: Is a permission required?
50 | eu-lobby:
51 | strategy: balance
52 | can-reconnect: true
53 | restricted: false
54 |
55 | game-xy:
56 | strategy: balance
57 | can-reconnect: false
58 | restricted: false
59 |
60 | private:
61 | strategy: balance
62 | can-reconnect: true
63 | restricted: false
64 |
65 | us-lobby:
66 | strategy: balance
67 | can-reconnect: true
68 | restricted: false
69 |
70 | # The default group a user is connected to if he freshly joins or his group was restricted in re-connections.
71 | # And not forced host is matching
72 | # Type: String
73 | default-group: eu-lobby
74 |
75 | # Commands that players can execute to join a certain group
76 | # Type Section
77 | join-commands:
78 | lobby: eu-lobby
79 | hub: eu-lobby
80 |
81 | # Setting force hosts. Use {dot} placeholder for dots to not break yaml syntax
82 | # Type: Section
83 | forced-hosts:
84 | "us{dot}mynetwork{dot}net": "us-lobby"
85 | "eu{dot}mynetwork{dot}net": "eu-lobby"
86 |
--------------------------------------------------------------------------------
/src/main/resources/container-inspector.yml:
--------------------------------------------------------------------------------
1 | # Is the docker listener enabled? This is the core functionality and should stay enabled
2 | # Type: Boolean
3 | enabled: true
4 |
5 | # Outputs extra information
6 | # Type: Boolean
7 | debug: true
8 |
9 | # Backend type (docker or kubernetes)
10 | backend: docker
11 |
12 | # Ensure the read permissions
13 | # available schemas: tcp or unix
14 | # Type: String
15 | docker:
16 | host: unix:///var/run/docker.sock
17 |
18 | # Type: Boolean
19 | tsl-verify: false
20 |
21 | # Type: String|null
22 | cert-path: ~
23 |
24 | # Type: Section
25 | registry:
26 | # Type: String|null
27 | username: ~
28 | # Type: String|null
29 | password: ~
30 | # Type: String|null
31 | email: ~
32 | # Type: String|null
33 | url: ~
34 |
35 | # The network name to resolve IP addresses
36 | # Only one network is possible to avoid confusion about which ip to use
37 | # check your networks with `docker network ls`
38 | network: "minecraft_local"
39 |
40 | kubernetes:
41 | # The namespace that bungeecord and all minecraft servers reside in
42 | namespace: minecraft
--------------------------------------------------------------------------------
/src/main/resources/plugin-notifier.yml:
--------------------------------------------------------------------------------
1 | # If you disable it no plugin which depends on this data will work
2 | # Type: Boolean
3 | enabled: true
4 |
5 | # Outputs extra information
6 | # Type: Boolean
7 | debug: true
8 |
9 | # Server updates will be sent after any add or remove anyway but the interval is usefull to update i.e. ProxiedPlayer Information
10 | # Type: Integer
11 | refresh-interval: 30
12 |
13 | # Maps environment variables and forwards them with the specified key to the bukkit client plugin
14 | # ie. docker run -e CATEGORY=factions
15 | # Reserved keys as they will be handed down anyway: name, address, motd, restricted (In case you want to overwrite you can do so)
16 | # Type: Section
17 | # Type MetaDataConfig:
18 | # - environment-variable (string): The environemnt variable to access
19 | # - required (bool): Is this value required? If not given and no default
20 | # is defined the server will not be added to connectionbalancer
21 | # - default (string): If the environment variable is not defined will fall
22 | # back to the given default
23 | meta-data-mapper:
24 | # make the TYPE also accessible
25 | type:
26 | required: true
27 | environment-variable: TYPE
28 |
29 | # Category for i.e create server selector menus based on categories
30 | category:
31 | environment-variable: SERVER_CATEGORY
32 | required: true
33 | default: "none"
34 |
35 | tags:
36 | environment-variable: SERVER_TAGS
37 | required: true
38 | default: ""
39 |
--------------------------------------------------------------------------------
/src/main/resources/server-updater.yml:
--------------------------------------------------------------------------------
1 | # Is the Server update enabled?
2 | # Type: Boolean
3 | enabled: true
4 |
5 | # Extra output
6 | # Type: Boolean
7 | debug: false
8 |
9 | # Container events actions to listen on for adding server
10 | # i.e.: "start", "health-status: healthy"
11 | # Recommended "start", "bootstrap" and health checks
12 | # the "bootstrap" events is triggered when connectionbalancer starts to register all running containers. Should ne be removed
13 | # If you want to add only healthy containers be aware of removing "start" action
14 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
15 | # Type: List
16 | add-actions:
17 | - "bootstrap"
18 | - "start"
19 | - "health_status: healthy"
20 | - "unpause"
21 |
22 | # Container events actions to listen on for remving server
23 | # i.e.: "kill", "die"
24 | # Recommended "die" and health checks
25 | # If you want to remove unhealthy containers add i.e: "health_status: unhealthy"
26 | # @see https://docs.docker.com/engine/reference/commandline/events/#object-types
27 | # Type: List
28 | remove-actions:
29 | - "kill"
30 | - "die"
31 | - "stop"
32 | - "pause"
33 |
34 |
35 |
36 | # Environment variables of the containers
37 | # Type: Section
38 | environment-variables:
39 | # The events listener will only add server with the defined environment variable
40 | # ie. docker run -e TYPE=minecraft_spigot my_server
41 | # Type: String
42 | identifier: TYPE
43 |
44 | # Be default the first exposed port is taken if the container exposes multiple ports you can
45 | # set it by setting the PORT environment variable in the container
46 | # If you exposed multiple ports its highly recommended to set the environment variable
47 | # Type: String
48 | port: SERVER_PORT
49 |
50 | # Setting the motd in the Bungeecord setting
51 | # i.e. docker run -e SERVER_FORCED_HOST="Another Minecraft Server" playerworld:latest
52 | # Type: String
53 | motd: SERVER_MOTD
54 |
55 | # Setting the server to restricted
56 | # If not set it is false, only excepts: "restricted" or "unrestricted"
57 | # i.e. docker run -e SERVER_RESTRICTED=true playerworld:latest
58 | # Type: String
59 | restricted: SERVER_RESTRICTED
60 |
61 | # Each server name needs to be unique
62 | # If you are not able to control if it is unique (autoscaling or whatever) you should not set it in your container
63 | # If you do not set the environment variable the container name itself will be used
64 | # Two server with the same name will overwrite each other
65 | # Type: String
66 | name: SERVER_NAME
67 |
--------------------------------------------------------------------------------