· 6 years ago · May 02, 2019, 06:38 PM
1# OpenSectors  [](https://travis-ci.org/SocketByte/OpenSectors)
2Join our Discord server!
3
4[](https://discord.gg/GtnYegP)
5
6OpenSectors allows you to split your world into different servers
7using Kryonet and other networking solutions. This plugin splits your
8world into few pieces and each piece is different server. Everything
9is properly synchronized so it feels like it is one server. It allows you to
10have more players playing on one world, because it is actually working
11on few servers, so nothing will stop you from having 3000+ players on a single
12world!
13
14It also supports basic authentication systems like checking password
15during the sector connection. That way people who want to
16connect using their own software to **your server** and do something
17bad to it, can't do it!
18
19This presentation will be quite long, so here you have quick links to everything.
20- [Features](#features)
21- [Future of the project](#upcoming-features)
22- [How it works?](#how-it-works--installation)
23- [Do I need redis?](#do-i-need-redis-or-something-similar)
24- [How do I use it?](#how-do-i-use-it-with-my-other-plugins)
25- [Configuration](#configuration)
26- [API](#api)
27- [Contribute or Donate](#contribution)
28- [License](#license)
29
30## Features
31* Basic time synchronization over all the servers
32* Going through sector border and "teleporting" between sectors
33* Super basic chat system
34* Simple, but powerful API
35* Performance (**Kryonet / Kryo** instead of Redis)
36* Configuration (with JSON support)
37* Security
38* Advanced logging systems
39* ActionBar support
40* Built-in fast MySQL connection and API (using HikariCP)
41* Possibilities
42
43## Upcoming features
44- Create a border (from MC1.8) on each sector
45- More informations in `PacketPlayerInfo`
46- More default packets to use
47- Multiple center sectors to improve performance (sector channel system)
48- Better time/weather system
49
50## How it works / installation?
51The project actually contains two projects - linker and system.
52Both are really important for everything to work.
53
54You put `OpenSectorLinker` on each of your sectors,
55and `OpenSectorSystem` on your BungeeCord (or one of it's forks) server.
56
57Both contain useful configurations, and you should check them!
58
59Remember, you want to unlock port `23904` and `23905` (or other you specify in configuration)
60because they're used to send data over the sector network.
61
62## Do I need redis or something similar?
63**No**! Absolutely not. It's main advantage over it's competitors
64is - no additional libraries or unconventional databases needed. It's just... plug & play.
65
66You just need a MySQL server which is pretty standard today.
67
68## How do I use it with my other plugins?
69You need to rewrite them or ask developers to do it for you.
70There's no way to make this differently. 70% of plugins need to be
71synchronized over the sector network to work properly.
72
73Fortunatelly, `OpenSectors` provide super easy API which
74makes this task much easier for end-user.
75
76You can edit existing plugins to make them work with `OpenSectors`
77which will require minimal Java and networking knowledge.
78
79**_But!_** I can edit or write plugins which will support `OpenSectors`
80for you! You need to contact me directly though.
81
82## Configuration
83Configuration is pretty simple. The main problem is to provide
84valid coordinates for your sectors. You can use ready to use configuration
85below, or create your own. Unfortunatelly, `OpenSectors` don't provide
86ANY of location scaling systems or similar currently.
87#### Linker configuration
88```yaml
89 server-id: 0
90 proxy-address: "127.0.0.1"
91 proxy-port-tcp: 23904
92 proxy-port-udp: 23905
93 proxy-connection-timeout: 5000
94 bufferSize: 8192 # Buffer size, increase it to send larger packets
95 kryonet-logging: NONE
96 auth-password: "CHANGE_THIS_TO_YOUR_PASSWORD___IT_NEEDS_TO_BE_SECURE!"
97 # Messages
98 sector-welcome-message:
99 - ''
100 - ' &aWelcome on sector &7%name%&a (id: &7%id%&a)'
101 - ''
102 sector-break-message: '&cYou can not destroy blocks that close to the sector border!'
103 sector-place-message: '&cYou can not build blocks that close to the sector border!'
104 sector-ignite-message: '&cYou can not ignite blocks that close to the sector border!'
105 sector-bucket-message: '&cYou can not pour out water/lava that close to the sector border!'
106 sector-border-close: '&2&lSector border: &f&l%distance%'
107 ```
108#### System configuration
109```json
110{
111 "password": "CHANGE_THIS_TO_YOUR_PASSWORD___IT_NEEDS_TO_BE_SECURE!",
112 "sectors": 5,
113 "portTCP": 23904,
114 "portUDP": 23905,
115 "bufferSize": 8192,
116 "bukkitTimeFrequency": 500,
117 "bukkitTimeIncremental": 10,
118 "border": 3000,
119 "defaultChatFormat": "&7{PLAYER}&8: &f{MESSAGE}",
120 "sqlController": {
121 "host": "localhost",
122 "port": 3306,
123 "user": "root",
124 "password": "",
125 "database": "opensectors",
126 "useDefaultSql": true
127 },
128 "serverControllers": [
129 {
130 "id": 0,
131 "offset": 0,
132 "port": 25566,
133 "name": "center",
134 "minX": -250.0,
135 "minZ": -250.0,
136 "maxX": 250.0,
137 "maxZ": 250.0
138 },
139 {
140 "id": 1,
141 "offset": 2,
142 "port": 25567,
143 "name": "n",
144 "minX": -250.0,
145 "minZ": -2000.0,
146 "maxX": 2000.0,
147 "maxZ": -250.0
148 },
149 {
150 "id": 2,
151 "offset": 3,
152 "port": 25568,
153 "name": "s",
154 "minX": -2000.0,
155 "minZ": 250.0,
156 "maxX": 250.0,
157 "maxZ": 2000.0
158 },
159 {
160 "id": 3,
161 "offset": 4,
162 "port": 25569,
163 "name": "e",
164 "minX": 250.0,
165 "minZ": -250.0,
166 "maxX": 2000.0,
167 "maxZ": 2000.0
168 },
169 {
170 "id": 4,
171 "offset": 5,
172 "port": 25570,
173 "name": "w",
174 "minX": -2000.0,
175 "minZ": -2000.0,
176 "maxX": -250.0,
177 "maxZ": 250.0
178 }
179 ]
180}
181```
182#### BungeeCord configuration
183```yaml
184servers:
185 center:
186 motd: OpenSectors
187 address: localhost:25566
188 restricted: false
189 n:
190 motd: North Sector
191 address: localhost:25567
192 restricted: false
193 s:
194 motd: South Sector
195 address: localhost:25568
196 restricted: false
197 w:
198 motd: West Sector
199 address: localhost:25569
200 restricted: false
201 e:
202 motd: East Sector
203 address: localhost:25570
204 restricted: false
205```
206## API
207Yes, that's what you wanted, don't ya? :D
208
209API is fairly easy, and with Javadocs/code on github it's even easier.
210But I will tell you how to do basic things using `OpenSectors` API system.
211API will be definitely richer in the future, remember that!
212
213#### Linker
214Add this code to your maven dependencies.
215```xml
216 <repositories>
217 <repository>
218 <id>socketbyte-repo</id>
219 <url>http://repo.socketbyte.pl/repository/nexus-releases</url>
220 </repository>
221 </repositories>
222
223 <dependencies>
224 <dependency>
225 <groupId>pl.socketbyte</groupId>
226 <artifactId>OpenSectorLinker</artifactId>
227 <version>1.2</version>
228 <scope>provided</scope>
229 </dependency>
230 </dependencies>
231```
232And of course Spigot/Bukkit engine too!
233#### System
234or this code when you make plugin for system:
235```xml
236 <repositories>
237 <repository>
238 <id>socketbyte-repo</id>
239 <url>http://repo.socketbyte.pl/repository/nexus-releases</url>
240 </repository>
241 </repositories>
242
243 <dependencies>
244 <dependency>
245 <groupId>pl.socketbyte</groupId>
246 <artifactId>OpenSectorSystem</artifactId>
247 <version>1.4</version>
248 <scope>provided</scope>
249 </dependency>
250 </dependencies>
251```
252
253There is few types of API calls you can make.
254Firstly, I would want you to know about packet catching and sending. It is
255very important in the long term. You want to send a lot of packets and data, and catch it!
256
257##### This part of the API (packet sending/catching) is also working on system side!
258
259You can register your own `Packet` extended classes easily,
260and send them over the network (and/or catch them in the listener)
261It is fairly easy, look;
262```java
263PacketExtender<PacketExampleTest> packetExtender = SectorAPI.createPacketExtender(PacketExampleTest.class);
264packetExtender.setPacketAdapter((connection, packet) ->
265 System.out.println("Received PacketExampleTest from the proxy server."));
266```
267In order this to work, you need to register this class on system plugin too.
268```java
269SectorAPI.register(PacketExampleTest.class);
270```
271or just create `PacketExtender` like above.
272
273What is `PacketExampleTest`? It's a class, which extend `Packet` class.
274```java
275public class PacketExampleTest extends Packet {
276
277 private String test;
278
279 public PacketExampleTest() {
280
281 }
282
283 public String getTest() {
284 return test;
285 }
286
287 public void setTest(String test) {
288 this.test = test;
289 }
290
291}
292```
293Remember, it needs to be absolutely the same on both sides.
294Congratulations! Now you can send it using:
295```java
296PacketExampleTest exampleTest = new PacketExampleTest();
297exampleTest.setTest("Hello, World!");
298SectorAPI.sendTCP(exampleTest);
299```
300You can also send UDP packets.
301```java
302SectorAPI.sendUDP(exampleTest);
303```
304
305You can also register something like pub/sub in Redis. What do I mean?
306You can register specific channels and send custom payload packet which contain some data.
307It is less convinient than using `PacketExtender` but it can be better sometimes.
308```java
309SectorAPI.registerPayloadChannel("TEST_CHANNEL", (connection, packet) -> {
310 System.out.println("Received PacketCustomPayload (TEST_CHANNEL) as a LINKER.");
311 System.out.println(packet);
312});
313```
314You can do the same on the system side. (You don't need to register anything!)
315Now you can send something, like
316```java
317PacketCustomPayload customPayload = new PacketCustomPayload();
318customPayload.setChannel("TEST_CHANNEL");
319customPayload.setData(new Object[] { "Hello", "World!" });
320SectorAPI.sendTCP(customPayload);
321```
322
323##### This part is only for Linkers (who would want to use it on system side though)
324This is... MySQL API! How awesome, right?
325Firstly, you need to make a Query packet.
326```java
327PacketQuery query = new PacketQuery();
328query.setQuery("INSERT INTO something VALUES(?, ?)");
329```
330...set replacements...
331```java
332query.addReplacement(1, "a nice value");
333query.addReplacement(2, "even nicer value");
334```
335... and send them!
336```java
337SectorAPI.sendTCP(query);
338```
339Congratulations, you executed a query on the system side
340and now your values are in the global database! Amazing!
341But, what if you want a result...? like... `ResultSet`?
342It works with this API too!
343
344Make an execute query
345```java
346PacketQueryExecute queryExecute = new PacketQueryExecute();
347queryExecute.setQuery("SELECT * FROM something WHERE fancier=?");
348queryExecute.addReplacement(1, "even nicer value");
349```
350... and!
351```java
352SectorAPI.sendTCP(queryExecute, packetQueryExecute -> {
353 SerializableResultSet resultSet = packetQueryExecute.getResultSet();
354 while (resultSet.next()) {
355 System.out.println("Received from SQL: " + resultSet.getString(0)
356 + " -> " + resultSet.getString(1));
357 }
358});
359```
360You create a query which contains an interface which is your callback.
361Confusing, right? Not really, it just returns you a modified packet
362with `SerializableResultSet` (something which is similar to real `ResultSet` but cached and serializable)
363
364That's all for the basic API part! You have some other functions but for that
365you need to check javadocs!
366
367You can get javadocs here: https://socketbyte.pl/javadocs/
368
369(and select proper project)
370
371### How to make your own database?
372The newest API allows you to set the database (like MariaDB, PostgreSQL, OrientDB or SQLite)
373
374How to do it? It is very simple.
375Set `useDefaultSql` value in JSON (system) configuration to `false`.
376
377This code needs to be in `onEnable()` of your addon plugin.
378```java
379HikariMySQL hikari = new HikariMySQL();
380hikari.setHost("localhost");
381hikari.setDatabase("database");
382hikari.setPort(3306);
383hikari.setPassword("");
384hikari.setUser("root");
385hikari.apply();
386
387HikariManager.INSTANCE.connect();
388```
389Congratulations, you did a standard MySQL connection.
390But what about others? I've prepared 4 most popular SQL configurations for Hikari.
391
392Just change the class like this:
393```java
394HikariMariaDb hikari = new HikariMariaDb();
395hikari.setHost("localhost");
396hikari.setDatabase("database");
397hikari.setPort(3306);
398hikari.setPassword("");
399hikari.setUser("root");
400hikari.apply();
401
402HikariManager.INSTANCE.connect();
403```
404You can do the same with:
405```java
406new HikariSQLite();
407new HikariPostgreSQL();
408```
409Remember, SQLite doesn't need port, password or user.
410You need only to set database, which is your file name.
411
412What about basic table creation? There's an API for that too!
413```java
414hikariMySQL.setTableWork(connection -> {
415 try {
416 PreparedStatement statement = connection.prepareStatement(
417 "CREATE TABLE IF NOT EXISTS...");
418 statement.executeUpdate();
419 statement.close();
420 } catch (SQLException e) {
421 e.printStackTrace();
422 }
423 finally {
424 try {
425 connection.close();
426 } catch (SQLException e) {
427 e.printStackTrace();
428 }
429 }
430});
431```
432Simple as that, then activate it by using:
433```java
434HikariManager.INSTANCE.createBasicTables();
435```
436You can also create your own database configurations for other SQL distributions.
437You can do that in two ways. Do a custom class which extend `HikariExtender`, or use `HikariWrapper`.
438
439#### HikariExtender
440Make a class and do `extends HikariExtender`, apply, and set everything.
441You can access `HikariDataSource` by using `getDataSource()` and every other
442setting using `getPassword()` or `getHost()`.
443
444Example (SQLite class):
445```java
446public class HikariSQLite extends HikariExtender {
447 @Override
448 protected void connect() {
449 HikariDataSource dataSource = getDataSource();
450 String database = getDatabase();
451
452 // Do everything you want here.
453 dataSource.setJdbcUrl("jdbc:sqlite:" + database + ".db");
454 dataSource.setDriverClassName("org.sqlite.JDBC");
455 dataSource.setPoolName("HikariSQLite");
456 dataSource.setMaxLifetime(0);
457 dataSource.setMaxLifetime(60000);
458 dataSource.setIdleTimeout(45000);
459 dataSource.setMaximumPoolSize(20);
460
461 Properties properties = new Properties();
462 properties.put("driverType", "thin");
463 dataSource.setDataSourceProperties(properties);
464 }
465}
466```
467#### HikariWrapper
468HikariWrapper is a class that allows you to do everything like above,
469but without creating a new class. Why? Because you can!
470
471Example:
472```java
473HikariWrapper hikariWrapper = new HikariWrapper(hikariWrapper1 -> {
474 HikariDataSource dataSource = hikariWrapper1.getDataSource();
475
476 dataSource.setJdbcUrl("...");
477 dataSource.setDriverClassName("...");
478});
479hikariWrapper.apply();
480
481HikariManager.INSTANCE.connect();
482```
483More about `HikariCP` here:
484
485https://github.com/brettwooldridge/HikariCP
486
487## Callback system
488It's easy to use system for sending, and instantly receiving packets (and doing something with them)
489It's based on a simple `Callback<T>` and `CompletableFuture<T>` which makes it really convinient.
490
491Basic usage:
492```java
493PacketPlayerState state = new PacketPlayerState();
494state.setPlayerName("...AnyPlayerName...");
495SectorAPI.sendTCP(state, new Callback<PacketPlayerState>() {
496 @Override
497 public void execute(PacketPlayerState packetPlayerState) {
498 // Your callback (information from the proxy)
499
500 System.out.println("Sector: " + packetPlayerState.getServerId());
501 System.out.println("Is Online: " + packetPlayerState.isOnline());
502 System.out.println("UniqueID: " + packetPlayerState.getPlayerUniqueId());
503 }
504});
505```
506This system also works on `PacketQueryExecute`.
507More packets based on callback system to come soon!
508
509## Task system
510There's pretty basic task system, but it allows you to create tasks
511which are synchronized over the network, so they're executed simultaneously.
512
513##### Basic task
514
515To create a task, use code below:
516```java
517SectorAPI.createTask(0,
518 () -> System.out.println("Synchronized task with id:0"),
519 0, 1, TimeUnit.SECONDS);
520```
521The first parameter (`0`) is a task id. Each task
522should have a different one!
523
524Second parameter is a runnable, which is your code.
525
526Then you have (`0`) which is `initialDelay`.
527
528Then is a `period` which is the rate of the task.
529
530And the last parameter is `TimeUnit` which doesn't need much explaining.
531
532##### Singleshot task
533There is also a singleshot task system, you can execute a code
534simultaneously on all sectors using `task.send()`.
535
536To prepare a singleshot task use:
537```java
538SynchronizedTask task = SectorAPI.prepareSingleShot(1,
539 () -> System.out.println("Single shot task executed!"));
540```
541And then send it using
542```java
543task.send();
544```
545You can do that as many times as you want.
546
547#### Disclaimer
548Remember that if you use task system, you need to install
549your plugin on all sectors.
550
551## Synchronized data types
552The new WIP feature of `OpenSectors` API.
553
554It allows you to create synchronized lists or maps over the network.
555It's super easy to use because of direct `List<E>` or `Map<K, V>` implementation.
556
557```java
558List<String> list = new SynchronizedList<>(0);
559Map<String, String> map = new SynchronizedMap<>(0);
560```
561The parameter is an ID, it needs to be unique for each list/map.
562Then, you just use them like normal lists or maps.
563
564Remember, this is a **Work In Progress** feature. It can be bugged, especially maps (!).
565
566## Wrapp integration
567More about wrapp [here](https://github.com/SocketByte/Wrapp).
568
569You can easily integrate Wrapp to OpenSectors. How? Let me explain.
570
571Start with registering an adapter:
572```java
573WrapperFactory wrapperFactory = new WrapperFactory(); // REUSE THAT
574SectorAPI.registerPayloadChannel("MYOBJECT_WRAPP_CHANNEL", (connection, packet) -> {
575 PacketWrapper<MyObject> packet = (PacketWrapper)packet;
576 Wrapper<MyObject> wrapper = packet.getWrapper();
577
578 MyObject myObject = new MyObject();
579 wrapperFactory.read(wrapper, myObject);
580
581 // congratz, your object is now set!
582});
583```
584Then you can send it from `Linker` using:
585```java
586MyObject objectToSave = new MyObject(); // it's not serializable
587
588WrapperFactory wrapperFactory = new WrapperFactory(); // REUSE THAT
589wrapperFactory.register(MyObject.class); // optional to improve speed
590Wrapper<MyObject> wrapper = wrapperFactory.write(objectToSave);
591
592PacketWrapper<MyObject> packet = new PacketWrapper<>();
593packet.setChannel("MYOBJECT_WRAPP_CHANNEL");
594packet.setData(new Object[] { "additional data" });
595packet.setWrapper(wrapper);
596
597SectorAPI.sendTCP(packet);
598```
599
600## Additional packets
601##### PacketItemTransfer *(from 1.1)*
602```java
603// Create packet
604PacketItemTransfer packet = new PacketItemTransfer();
605// Set receiver (all == every player on every sector)
606packet.setReceiver(Receiver.ALL);
607// Set itemstack as SerializableItem
608packet.setItemStack(new SerializableItem(new ItemStack(...)));
609```
610##### PacketSendMessage *(from 1.1)*
611```java
612// Create packet
613PacketSendMessage packet = new PacketSendMessage();
614// Set receiver (player == only one player)
615packet.setReceiver(Receiver.PLAYER, "uniqueId");
616// Set message and it's type
617packet.setMessage(MessageType.ACTION_BAR, "&6&lHello, World!");
618```
619##### PacketPlayerTransfer *(from 1.0)*
620```java
621PacketPlayerTransfer packet = new PacketPlayerTransfer();
622// Set the player
623packet.setPlayerUniqueId("uniqueId");
624// Set player info (not required)
625packet.setPlayerInfo(new PacketPlayerInfo(player));
626// Set server id
627packet.setServerId(1);
628```
629##### PacketPlayerTeleport *(from 1.2)*
630```java
631PacketPlayerTeleport packet = new PacketPlayerTeleport();
632// Set player
633packet.setPlayerUniqueId("uniqueId");
634// Set target (optional)
635packet.setTargetUniqueId("uniqueId");
636// Or set coords
637packet.setLocation(250, 70, 250);
638```
639##### PacketPlayerState *(from 1.2)*
640```java
641PacketPlayerState packet = new PacketPlayerState();
642packet.setPlayerName("...");
643SectorAPI.sendTCP(packet, packetPlayerState -> {
644 // do what you want with the callback
645});
646```
647
648More to come soon!
649
650## Contribution
651Yes! Of course you can contribute in `OpenSectors`
652
653Either helping with code, or supporting the project with donation.
654
655You can donate here:
656
657[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=H5N9GMVH464Y8)
658
659You will be mentioned in the `README.md` file as soon as you donate something. (Every donation really appreciated)
660
661## License
662Project is licensed under `Creative Commons License (CC BY-NC 3.0)` as you're
663not permitted to use open-source version of this project in commercial applications.