diff --git a/README.md b/README.md index 4880c64a..ad6e6be4 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,10 @@ display-ticking-errors-to-players: true # Если серверный тик прерывается слишком часто, сервер будет остановлен; # в противном случае тик будет пытаться продолжать перезапуски. stop-on-critical-ticking-error: false + +# Максималльное количесво пакетов которые может хранить сервер для отправки игроку +# Если количесвто пакетов привышает это, то игрок кикается с сервера +max-packet-sending: 200 ``` ## Консольные команды diff --git a/src/main/java/com/reider745/InnerCoreServer.java b/src/main/java/com/reider745/InnerCoreServer.java index bf1b4033..f8ab14b3 100644 --- a/src/main/java/com/reider745/InnerCoreServer.java +++ b/src/main/java/com/reider745/InnerCoreServer.java @@ -469,6 +469,10 @@ public class InnerCoreServer { return getPropertyBoolean("auto-save-world", true); } + public static int getMaxPacketSending(){ + return getPropertyInt("max-packet-sending", 200); + } + public static boolean isUnsupportedOptionsAllowed() { return getPropertyBoolean("allow-unsupported-options", false); } diff --git a/src/main/java/com/reider745/event/EventListener.java b/src/main/java/com/reider745/event/EventListener.java index 2e90c78e..6697de06 100644 --- a/src/main/java/com/reider745/event/EventListener.java +++ b/src/main/java/com/reider745/event/EventListener.java @@ -21,15 +21,7 @@ import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.inventory.CraftItemEvent; import cn.nukkit.event.inventory.InventoryPickupItemEvent; import cn.nukkit.event.level.ChunkPopulateEvent; -import cn.nukkit.event.player.PlayerCommandPreprocessEvent; -import cn.nukkit.event.player.PlayerDeathEvent; -import cn.nukkit.event.player.PlayerEatFoodEvent; -import cn.nukkit.event.player.PlayerExperienceChangeEvent; -import cn.nukkit.event.player.PlayerFormRespondedEvent; -import cn.nukkit.event.player.PlayerInteractEntityEvent; -import cn.nukkit.event.player.PlayerInteractEvent; -import cn.nukkit.event.player.PlayerLoginEvent; -import cn.nukkit.event.player.PlayerPreLoginEvent; +import cn.nukkit.event.player.*; import cn.nukkit.event.redstone.RedstoneUpdateEvent; import cn.nukkit.event.server.ServerCommandEvent; import cn.nukkit.form.response.FormResponse; @@ -425,20 +417,20 @@ public class EventListener implements Listener { } @EventHandler(priority = EventPriority.HIGHEST) - public void onLoginEvent(PlayerLoginEvent event) { + public void onLoginEvent(PlayerJoinEvent event) { ConnectedClient client = PlayerHooks.getForPlayer(event.getPlayer()); if (client == null) { - event.setKickMessage("You are should connect via Inner Core!"); - event.setCancelled(); + event.getPlayer().kick("You are should connect via Inner Core!"); + // event.setCancelled(); return; } final String username = event.getPlayer().getName(); consumeEvent(event, () -> NativeCallback.onPlayerLogin(client, username, (message) -> { if (message != null) { - event.setKickMessage(message); + event.getPlayer().kick(message); } - event.setCancelled(); + // event.setCancelled(); })); } diff --git a/src/main/java/com/reider745/hooks/RhinoOverrides.java b/src/main/java/com/reider745/hooks/RhinoOverrides.java index ad1af108..594c3e93 100644 --- a/src/main/java/com/reider745/hooks/RhinoOverrides.java +++ b/src/main/java/com/reider745/hooks/RhinoOverrides.java @@ -16,9 +16,6 @@ import com.reider745.api.hooks.HookClass; import com.reider745.api.hooks.TypeHook; import com.reider745.api.hooks.annotation.Hooks; import com.reider745.api.hooks.annotation.Inject; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Symbol; @Hooks(className = "org.mozilla.javascript.JavaMembers") public class RhinoOverrides implements HookClass { diff --git a/src/main/java/com/reider745/network/innercore/PlayerSocketSendingThread.java b/src/main/java/com/reider745/network/innercore/PlayerSocketSendingThread.java new file mode 100644 index 00000000..95164c5a --- /dev/null +++ b/src/main/java/com/reider745/network/innercore/PlayerSocketSendingThread.java @@ -0,0 +1,68 @@ +package com.reider745.network.innercore; + +import com.reider745.InnerCoreServer; +import com.zhekasmirnov.apparatus.multiplayer.channel.data.DataPacket; +import com.zhekasmirnov.apparatus.multiplayer.channel.data.SocketDataChannel; +import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.innercore.api.log.ICLog; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class PlayerSocketSendingThread extends Thread { + private BlockingQueue list = new LinkedBlockingQueue<>(); + + private final DataOutputStream outputStream; + private final SocketDataChannel channel; + + public PlayerSocketSendingThread(Socket socket, SocketDataChannel channel) throws IOException { + this.outputStream = new DataOutputStream(socket.getOutputStream()); + this.channel = channel; + + start(); + } + + public void send(DataPacket packet){ + list.add(packet); + } + + @Override + public void run() { + while (true){ + try{ + if(channel.isClosed()) break; + + if(list.size() > 150){ + channel.close(); + break; + } + + DataPacket packet = list.take(); + + outputStream.writeUTF(packet.name); + outputStream.writeByte(packet.formatId); + outputStream.writeInt(packet.data.length); + outputStream.write(packet.data); + + Logger.debug("send "+packet.name); + + }catch (Exception e){ + if(InnerCoreServer.isDeveloperMode()) + Logger.error(ICLog.getStackTrace(e)); + channel.close(); + } + } + } + + public void close(){ + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/DataChannel.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/DataChannel.java index 56cd882a..87ff3d9d 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/DataChannel.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/DataChannel.java @@ -29,20 +29,31 @@ public abstract class DataChannel { public interface IBrokenChannelListener { void onBroke(IOException exception); } - public void send(DataPacket packet) { - synchronized (sendLock) { - if (isClosed || isShutdown) { - return; - } - try { - sendImpl(packet); - } catch (IOException e) { - channelBroke(e); + if(canSendingLock()){ + synchronized (sendLock) { + if (isClosed || isShutdown) { + return; + } + try { + sendImpl(packet); + } catch (IOException e) { + channelBroke(e); + } } + + return; + } + + if (isClosed || isShutdown) { + return; + } + try { + sendImpl(packet); + } catch (IOException e) { + channelBroke(e); } } - public DataPacket receive() { synchronized (receiveLock) { try { @@ -107,9 +118,14 @@ public abstract class DataChannel { protected abstract void sendImpl(DataPacket packet) throws IOException; protected abstract DataPacket receiveImpl() throws IOException; protected abstract void closeImpl() throws IOException; + protected boolean canSendingLock(){ + return true; + }; + public abstract int getProtocolId(); public abstract String getClient(); + @Deprecated public void listenerLoop() { while (!isClosed) { DataPacket packet = receive(); @@ -123,6 +139,15 @@ public abstract class DataChannel { } } + public void onListener(DataPacket packet){ + synchronized (packetListeners) { + for (IPacketListener listener : packetListeners) { + listener.receive(packet); + } + } + } + + public void addListener(IPacketListener listener) { synchronized (packetListeners) { packetListeners.add(listener); diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/SocketDataChannel.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/SocketDataChannel.java index 29ba6332..0845027b 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/SocketDataChannel.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/channel/data/SocketDataChannel.java @@ -1,7 +1,8 @@ package com.zhekasmirnov.apparatus.multiplayer.channel.data; +import com.reider745.network.innercore.PlayerSocketSendingThread; + import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; @@ -9,41 +10,36 @@ public class SocketDataChannel extends DataChannel { public static final int PROTOCOL_ID = 1; public final Socket socket; - public final DataInputStream inputStream; - public final DataOutputStream outputStream; + + private final DataInputStream inputStream; + public final PlayerSocketSendingThread sendingThread; + public SocketDataChannel(Socket socket) throws IOException { this.socket = socket; - this.inputStream = new DataInputStream(socket.getInputStream()); - this.outputStream = new DataOutputStream(socket.getOutputStream()); socket.setKeepAlive(true); + inputStream = new DataInputStream(socket.getInputStream()); + this.sendingThread = new PlayerSocketSendingThread(socket, this); } @Override public void sendImpl(DataPacket packet) throws IOException { - if(socket.isClosed()) return; - synchronized (outputStream) { - outputStream.writeUTF(packet.name); - outputStream.writeByte(packet.formatId); - outputStream.writeInt(packet.data.length); - outputStream.write(packet.data); - } + sendingThread.send(packet); } @Override public DataPacket receiveImpl() throws IOException { - if(socket.isClosed()) return null; - synchronized (inputStream) { - String name = inputStream.readUTF(); - int formatId = inputStream.readByte(); - int size = inputStream.readInt(); - if (size < 0 || size > 1024 * 1024 * 64) { - throw new IOException("packet has invalid size " + size); - } - byte[] data = new byte[size]; - inputStream.readFully(data); - return new DataPacket(name, formatId, data); + String name = inputStream.readUTF(); + int formatId = inputStream.readByte(); + int size = inputStream.readInt(); + + if (size < 0 || size > 1024 * 1024 * 64) { + throw new IOException("packet has invalid size " + size); } + + byte[] data = new byte[size]; + inputStream.readFully(data); + return new DataPacket(name, formatId, data); } @Override @@ -53,11 +49,7 @@ public class SocketDataChannel extends DataChannel { } catch (IOException e) { e.printStackTrace(); } - try { - outputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } + sendingThread.close(); try { socket.shutdownOutput(); socket.shutdownInput(); @@ -66,6 +58,11 @@ public class SocketDataChannel extends DataChannel { } } + @Override + protected boolean canSendingLock() { + return false; + } + @Override public int getProtocolId() { return PROTOCOL_ID; diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java index 18884dd7..fe58071f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java @@ -1,5 +1,6 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; +import com.reider745.InnerCoreServer; import com.zhekasmirnov.apparatus.adapter.innercore.EngineConfig; import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.NativeAPI; @@ -26,10 +27,12 @@ public class WorldDataSaverHandler { private long previousAutoSave = 0; public void fetchParamsFromConfig() { + final int auto_save_period = InnerCoreServer.getAutoSavePeriod(); + setParams( - EngineConfig.getBoolean("background.auto_save", true), - EngineConfig.getBoolean("background.auto_save_world", true), - EngineConfig.getInt("background.auto_save_period", 30) * 1000 + auto_save_period >= 40, + InnerCoreServer.canAutoSaveWorld(), + auto_save_period * 1000 ); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java index 0facb884..69d0b245 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java @@ -67,6 +67,7 @@ public class WorldDataScopeRegistry { for (Map.Entry entry : scopeMap.entrySet()) { String key = entry.getKey(); Object data = json.opt(key); + if(key.isEmpty()) continue; Logger.debug("Loaded save "+key); try { entry.getValue().readJson(data); diff --git a/src/main/resources/zotecore.yml b/src/main/resources/zotecore.yml index c8493f1f..fb8dd663 100644 --- a/src/main/resources/zotecore.yml +++ b/src/main/resources/zotecore.yml @@ -52,3 +52,6 @@ pack-version-code: 153 # If ticking callback interrupts too often, server will be stopped; # otherwise it will continue trying restart itself continiously. # stop-on-critical-ticking-error: true + + +# max-packet-sending: 150 \ No newline at end of file