Skip to content
This repository has been archived by the owner on Dec 23, 2024. It is now read-only.

Commit

Permalink
Pause when world is loaded
Browse files Browse the repository at this point in the history
  • Loading branch information
pisceskkk committed Aug 11, 2021
1 parent 3ce48b5 commit 6691776
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 266 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@

Mindustry plugin to auto pause empty server.

## Features

- When the last player leave the server, the game will be paused.
- When the first player connect to the paused server, the game will be started.
Ideas from [Astro36/mindustry-pause-plugin](https://github.com/Astro36/mindustry-pause-plugin)

## TODOs
## Features

- [ ] When the empty server host a new game, the game will be paused automatically.
- When the last player leave the server, the game will be paused automatically.
- When the server host a new game with no player, the game will be paused automatically.
- When the first player connect to the paused server, the game will be resumed automatically.
- Command `autopause <on/off>` for server and client to control if the server pause automatically.

## Downloading

Expand Down
5 changes: 3 additions & 2 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "ServerAutoPause",
"author": "pisceskkk",
"main": "cn.pisceskkk.ServerAutoPause",
"description": "A plugin to auto pause(continue) server when empty(or not).",
"version": 1.0
"minGameVersion": "126.2",
"description": "A plugin to auto pause(resume) server when empty(or not).",
"version": 1.1
}
302 changes: 44 additions & 258 deletions src/cn.pisceskkk/ServerAutoPause.java
Original file line number Diff line number Diff line change
@@ -1,295 +1,81 @@
package cn.pisceskkk;

import arc.Events;
import arc.graphics.*;
import arc.struct.Seq;
import arc.util.CommandHandler;
import arc.util.Log;
import arc.util.Strings;
import arc.util.Time;
import arc.util.io.*;
import arc.util.serialization.Base64Coder;
import mindustry.*;
import mindustry.core.GameState.State;
import mindustry.core.Version;
import mindustry.game.EventType;
import mindustry.gen.*;
import mindustry.mod.*;
import mindustry.net.Administration;
import mindustry.net.Packets;
import mindustry.game.EventType.*;
import mindustry.net.Administration.*;

import java.io.DataOutputStream;
import java.nio.ByteBuffer;
import java.util.zip.CRC32;

import static arc.util.Log.err;
import static arc.util.Log.info;
import static mindustry.Vars.*;
import static mindustry.Vars.platform;

public class ServerAutoPause extends Plugin{
private ReusableByteOutStream writeBuffer = new ReusableByteOutStream(127);
private Writes outputBuffer = new Writes(new DataOutputStream(writeBuffer));
private boolean enabled = true;
private int playerCount = 0;

//called when game initializes
@Override
public void init(){
net.handleServer(Packets.Connect.class, (con, connect) -> {
Events.fire(new ConnectionEvent(con));

if(Vars.netServer.admins.isIPBanned(connect.addressTCP) || Vars.netServer.admins.isSubnetBanned(connect.addressTCP)){
con.kick(Packets.KickReason.banned);
return;
}
/*
check if there is no player and game is paused, then start the game.
*/
if(Groups.player.size() == 0 && Vars.state.serverPaused == true) {
String message = Strings.format("Game Started.");
if(Administration.Config.showConnectMessages.bool()) info(message);
Vars.state.serverPaused = false;
}
});


Vars.net.handleServer(Packets.Disconnect.class, (con, packet) -> {
if(con.player != null){
Vars.netServer.onDisconnect(con.player, packet.reason);
/*
check if there is no player, then pause the game.
*/
if(Groups.player.size() == 0) {
Vars.state.serverPaused = true;
String message = Strings.format("Game Paused.");
Events.on(EventType.WorldLoadEvent.class, event->{
if (enabled) {
if (playerCount == 0 && Vars.state.serverPaused == false) {
String message = Strings.format("Game Paused Automatically.");
if(Administration.Config.showConnectMessages.bool()) info(message);
Vars.state.serverPaused = true;
}
}
});


net.handleServer(Packets.ConnectPacket.class, (con, packet) -> {
if(con.kicked) return;

if(con.address.startsWith("steam:")){
packet.uuid = con.address.substring("steam:".length());
}

con.connectTime = Time.millis();

String uuid = packet.uuid;
byte[] buuid = Base64Coder.decode(uuid);
CRC32 crc = new CRC32();
crc.update(buuid, 0, 8);
ByteBuffer buff = ByteBuffer.allocate(8);
buff.put(buuid, 8, 8);
buff.position(0);
if(crc.getValue() != buff.getLong()){
con.kick(Packets.KickReason.clientOutdated);
return;
}

if(Vars.netServer.admins.isIPBanned(con.address) || Vars.netServer.admins.isSubnetBanned(con.address)) return;

if(con.hasBegunConnecting){
con.kick(Packets.KickReason.idInUse);
return;
}

PlayerInfo info = Vars.netServer.admins.getInfo(uuid);

con.hasBegunConnecting = true;
con.mobile = packet.mobile;

if(packet.uuid == null || packet.usid == null){
con.kick(Packets.KickReason.idInUse);
return;
}

if(Vars.netServer.admins.isIDBanned(uuid)){
con.kick(Packets.KickReason.banned);
return;
}

if(Time.millis() < Vars.netServer.admins.getKickTime(uuid, con.address)){
con.kick(Packets.KickReason.recentKick);
return;
}

if(Vars.netServer.admins.getPlayerLimit() > 0 && Groups.player.size() >= Vars.netServer.admins.getPlayerLimit() && !netServer.admins.isAdmin(uuid, packet.usid)){
con.kick(Packets.KickReason.playerLimit);
return;
}

Seq<String> extraMods = packet.mods.copy();
Seq<String> missingMods = mods.getIncompatibility(extraMods);

if(!extraMods.isEmpty() || !missingMods.isEmpty()){
//can't easily be localized since kick reasons can't have formatted text with them
StringBuilder result = new StringBuilder("[accent]Incompatible mods![]\n\n");
if(!missingMods.isEmpty()){
result.append("Missing:[lightgray]\n").append("> ").append(missingMods.toString("\n> "));
result.append("[]\n");
}

if(!extraMods.isEmpty()){
result.append("Unnecessary mods:[lightgray]\n").append("> ").append(extraMods.toString("\n> "));
Events.on(EventType.PlayerJoin.class, event -> {
if (enabled) {
if (playerCount == 0 && Vars.state.serverPaused == true) {
String message = Strings.format("Game Started Automatically.");
if(Administration.Config.showConnectMessages.bool()) info(message);
Vars.state.serverPaused = false;
}
con.kick(result.toString(), 0);
playerCount += 1;
}
});

if(!Vars.netServer.admins.isWhitelisted(packet.uuid, packet.usid)){
info.adminUsid = packet.usid;
info.lastName = packet.name;
info.id = packet.uuid;
Vars.netServer.admins.save();
Call.infoMessage(con, "You are not whitelisted here.");
info("&lcDo &lywhitelist-add @&lc to whitelist the player &lb'@'", packet.uuid, packet.name);
con.kick(Packets.KickReason.whitelist);
return;
}

if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !Vars.netServer.admins.allowsCustomClients())){
con.kick(!Version.type.equals(packet.versionType) ? Packets.KickReason.typeMismatch : Packets.KickReason.customClient);
return;
}

boolean preventDuplicates = headless && netServer.admins.isStrict();

if(preventDuplicates){
if(Groups.player.contains(p -> p.name.trim().equalsIgnoreCase(packet.name.trim()))){
con.kick(Packets.KickReason.nameInUse);
return;
}

if(Groups.player.contains(player -> player.uuid().equals(packet.uuid) || player.usid().equals(packet.usid))){
con.kick(Packets.KickReason.idInUse);
return;
Events.on(EventType.PlayerLeave.class, event -> {
if (enabled) {
playerCount -= 1;
if (playerCount == 0 && Vars.state.serverPaused == false) {
String message = Strings.format("Game Paused Automatically.");
if(Administration.Config.showConnectMessages.bool()) info(message);
Vars.state.serverPaused = true;
}
}

packet.name = fixName(packet.name);

if(packet.name.trim().length() <= 0){
con.kick(Packets.KickReason.nameEmpty);
return;
}

if(packet.locale == null){
packet.locale = "en";
}

String ip = con.address;

Vars.netServer.admins.updatePlayerJoined(uuid, ip, packet.name);

if(packet.version != Version.build && Version.build != -1 && packet.version != -1){
con.kick(packet.version > Version.build ? Packets.KickReason.serverOutdated : Packets.KickReason.clientOutdated);
return;
}

if(packet.version == -1){
con.modclient = true;
}

Player player = Player.create();
player.admin = Vars.netServer.admins.isAdmin(uuid, packet.usid);
player.con = con;
player.con.usid = packet.usid;
player.con.uuid = uuid;
player.con.mobile = packet.mobile;
player.name = packet.name;
player.locale = packet.locale;
player.color.set(packet.color).a(1f);

//save admin ID but don't overwrite it
if(!player.admin && !info.admin){
info.adminUsid = packet.usid;
}

try{
writeBuffer.reset();
player.write(outputBuffer);
}catch(Throwable t){
con.kick(Packets.KickReason.nameEmpty);
err(t);
return;
}

con.player = player;

//playing in pvp mode automatically assigns players to teams
player.team(Vars.netServer.assignTeam(player));

Vars.netServer.sendWorldData(player);

platform.updateRPC();

Events.fire(new PlayerConnect(player));

/*
check if there is no player and game is paused, then start the game.
*/
if(Groups.player.size() == 0 && Vars.state.serverPaused == true) {
String message = Strings.format("Game Started.");
if(Administration.Config.showConnectMessages.bool()) info(message);
Vars.state.serverPaused = false;
}

});

}

@Override
public void registerServerCommands(CommandHandler handler) {

}

String fixName(String name){
name = name.trim();
if(name.equals("[") || name.equals("]")){
return "";
}

for(int i = 0; i < name.length(); i++){
if(name.charAt(i) == '[' && i != name.length() - 1 && name.charAt(i + 1) != '[' && (i == 0 || name.charAt(i - 1) != '[')){
String prev = name.substring(0, i);
String next = name.substring(i);
String result = checkColor(next);
public void registerServerCommands(CommandHandler handler){
handler.register("autopause", "<on/off>", "Enabled/disable autopause", (arg, player) -> {
if (arg.length == 0) {
Log.info("[scarlet]Error: Second parameter required: 'on' or 'off'");
}

name = prev + result;
if (!(arg[0].equals("on") || arg[0].equals("off"))) {
Log.info("[scarlet]Error: Second parameter must be either 'on' or 'off'");
}
}

StringBuilder result = new StringBuilder();
int curChar = 0;
while(curChar < name.length() && result.toString().getBytes(Strings.utf8).length < maxNameLength){
result.append(name.charAt(curChar++));
}
return result.toString();
enabled = arg[0].equals("on");
});
}

String checkColor(String str){
for(int i = 1; i < str.length(); i++){
if(str.charAt(i) == ']'){
String color = str.substring(1, i);
public void registerClientCommands(CommandHandler handler) {
handler.<Player>register("autopause", "<on/off>", "Enabled/disable autopause", (arg, player) -> {
if (arg.length == 0) {
player.sendMessage("[scarlet]Error: Second parameter required: 'on' or 'off'");
}

if(Colors.get(color.toUpperCase()) != null || Colors.get(color.toLowerCase()) != null){
Color result = (Colors.get(color.toLowerCase()) == null ? Colors.get(color.toUpperCase()) : Colors.get(color.toLowerCase()));
if(result.a <= 0.8f){
return str.substring(i + 1);
}
}else{
try{
Color result = Color.valueOf(color);
if(result.a <= 0.8f){
return str.substring(i + 1);
}
}catch(Exception e){
return str;
}
}
if (!(arg[0].equals("on") || arg[0].equals("off"))) {
player.sendMessage("[scarlet]Error: Second parameter must be either 'on' or 'off'");
}
}
return str;
}

enabled = arg[0].equals("on");
});
}
}

0 comments on commit 6691776

Please sign in to comment.