Compare commits

..

No commits in common. "master" and "universal" have entirely different histories.

64 changed files with 643 additions and 1882 deletions

View file

@ -63,11 +63,9 @@ Hardcore hearts by chance
### Sleep ### Sleep
Sleeping doesn't skip night, but speeds it up. The more players, the faster it goes. Sleeping doesn't skip night, but speeds it up. The more players, the faster it goes.
- Instant sleep \ - Instant sleep \
One can instantly skip, but only a part of the night. \ One can instantly skip, but only a part of the night. \
There's 5 players on the server. A night is 10 minutes long. \ There's 5 players on the server. A night is 10 minutes long. \
Each player can instantly skip 2 minutes of the night at any time, even if others aren't sleeping Each player can instantly skip 2 minutes of the night at any time, even if others aren't sleeping
- Heal \
Sleeping heals
### Authentication ### Authentication
Players are given a unique subdomain like "\<key>.example.com" and they must use it to join \ Players are given a unique subdomain like "\<key>.example.com" and they must use it to join \
@ -83,21 +81,6 @@ Issue messages that the player needs to read to keep playing, and that make an a
`/emergencyalerts` (`tweaks724.emergencyalerts`) `/emergencyalerts` (`tweaks724.emergencyalerts`)
### Remote redstone
Adds a "gateway" item that are controlled over internet. \
[RETSTONE.md for more info](/Minecon724/tweaks724/src/branch/master/docs/RETSTONE.md)
### Knockback
Control knockback dealt by entities
### Kill switch
Quickly kills (terminates) the server on trigger, via command or HTTP request.
[KILLSWITCH.md for more info](/Minecon724/tweaks724/src/branch/master/docs/KILLSWITCH.md)
### Swing through grass
### Utility commands ### Utility commands
- `/ping` - displays player ping \ - `/ping` - displays player ping \

View file

@ -1,32 +0,0 @@
Killswitch immediately stops the server.
### Warning
This terminates the server process, meaning it's like you'd pull the power. \
So you will lose some progress (since the last auto save), or worst case your world (or other data) gets corrupted.
Terminal froze after kill? `reset`
### Over a command
No key is required. \
`/servkill` is the command. Permission: `tweaks724.servkill`. \
You must grant the permission manually, like with a permission plugin - it's not automatically assigned even to OPs.
### Over HTTP
HTTP is insecure, meaning others *could* intercept your request to the server and get your key. \
To encrypt, put this behind a proxy to get HTTPS or a VPN (directly to the server, not a commercial VPN) \
Or regenerate the key after every usage.
Make a GET request to `/key/<base64 encoded key>`
Example:
```
https://127.0.0.1:57932/key/lNwANMSZhLiTWhNxSoqQ5Q==
|_ server address _| |_ base64 encoded key _|
```
The response is a 404 no matter what. Either way, you will notice that the server has stopped.
The key is generated to `plugins/Tweaks724/storage/killswitch key` \
To use it with HTTP server, encode it to base64.
Rate limit is 1 request / 5 minutes

View file

@ -1,3 +0,0 @@
Here's the documentation.
Click above on a file to read more about a topic.

View file

@ -1,48 +0,0 @@
## Remote redstone
See [retstone.py](retstone.py) for usage example
### Glossary
gateways - the blocks (daylight detector) that read or emit redstone with a specified power, controlled with UDP packets \
packet - a bunch of bytes sent over the internet, that do something with a specified gateway
### Crafting
To craft a gateway, combine:
- nether star
- ender chest
- chorus flower
- daylight detector
### Usage
Each gateway has an ID assigned. To view it, shift right-click a gateway.
To destroy a gateway, use silk touch.
### How it works
A packet is int / 4 bytes / 32 bits
Packet format:
```
[ 01 ] [ 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ] [ 29 30 31 32 ]
action bits 2-28 of repeater id payload
```
- action: `1` to write, `0` to read
- payload: power level if writing
If writing, no response \
If reading, response is the power level, or 0 if not powered or no repeater with that ID (or not loaded)
Reading powers the block down of course \
BUT you should power it down (or read), wait some, and then read again
**WARNING** the socket is not ratelimited or protected in any way. \
Though it should be hard to bruteforce, because there's no feedback
### Retstone
**Network** translates to **reto** in Esperanto \
So retsomething means networked something (posto - mail, retposto - email, ejo - place (site), retejo - website, etc.) \
And sometimes we use network instead of internet, same is in that language \
Hence retstone

View file

@ -1,44 +0,0 @@
# Copyright (C) 2024 Minecon724
# Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
# in the project root for the full license text.
import socket, struct
ENDPOINT = ("127.0.0.1", 57931)
def get_power(repeater_id: int) -> int | None:
message = repeater_id & 0x7FFFFFF0
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(message.to_bytes(4), ENDPOINT)
return struct.unpack(">b", sock.recvfrom(1)[0])[0]
def set_power(repeater_id: int, power: int):
message = repeater_id & 0x7FFFFFF0
message |= 0x80000000
message |= power & 0xF
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(message.to_bytes(4), ENDPOINT)
if __name__ == "__main__":
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("repeater_id", help="The repeater ID", type=int)
parser.add_argument("-p", "--power", help="Power the repeater with specified power. Leave to read.", type=int)
parser.add_argument("-c", "--copy", help="Copy input of one repeater to another. If combined with power, power is added.", type=int)
args = parser.parse_args()
if args.copy is None:
if args.power is None:
power = get_power(args.repeater_id)
print(power)
else:
set_power(args.repeater_id, args.power)
else:
while True:
power = get_power(args.repeater_id)
set_power(args.copy, power)

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- <!--
~ Copyright (C) 2025 Minecon724 ~ Copyright (C) 2024 Minecon724
~ Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file ~ Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
~ in the project root for the full license text. ~ in the project root for the full license text.
--> -->
@ -10,7 +10,7 @@
<groupId>eu.m724</groupId> <groupId>eu.m724</groupId>
<artifactId>tweaks</artifactId> <artifactId>tweaks</artifactId>
<version>0.1.12-SNAPSHOT</version> <version>0.1.10-SNAPSHOT</version>
<properties> <properties>
<maven.compiler.source>21</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
@ -167,7 +167,7 @@
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
<version>1.21.1-R0.1-SNAPSHOT</version> <version>${project.spigot.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>

View file

@ -1,63 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DebugLogger {
static Logger logger;
public static void info(String message, Object... format) {
log(Level.INFO, message, format);
}
public static void warning(String message, Object... format) {
log(Level.WARNING, message, format);
}
public static void severe(String message, Object... format) {
log(Level.SEVERE, message, format);
}
public static void fine(String message, Object... format) {
log(Level.FINE, message, format);
}
public static void finer(String message, Object... format) {
log(Level.FINER, message, format);
}
private static void log(Level level, String message, Object... format) {
if (logger.getLevel().intValue() > level.intValue()) return;
var caller = Thread.currentThread().getStackTrace()[3].getClassName();
if (caller.equals(TweaksModule.class.getName())) {
var pcaller = Thread.currentThread().getStackTrace()[4].getClassName();
if (pcaller.endsWith("Module"))
caller = pcaller;
}
if (caller.startsWith("eu.m724.tweaks."))
caller = caller.substring(15);
message = "[" + caller + "] " + message.formatted(format);
if (level.intValue() < Level.INFO.intValue()) { // levels below info are never logged even if set for some reason
// colors text gray (cyan is close to gray)
if (level == Level.FINE) {
message = "\033[38;5;250m" + message + "\033[39m";
} else {
message = "\033[38;5;245m" + message + "\033[39m";
}
level = Level.INFO;
}
logger.log(level, message);
}
}

View file

@ -28,15 +28,11 @@ public class Language {
return INSTANCE.resourceBundle.getString(key); return INSTANCE.resourceBundle.getString(key);
} }
public static String getString(String key, Object... format) {
return INSTANCE.resourceBundle.getString(key).formatted(format);
}
public static BaseComponent getComponent(String key, ChatColor color) { public static BaseComponent getComponent(String key, ChatColor color) {
return new ComponentBuilder(getString(key)).color(color).build(); return new ComponentBuilder(getString(key)).color(color).build();
} }
public static BaseComponent getComponent(String key, ChatColor color, Object... format) { public static BaseComponent getComponent(String key, ChatColor color, Object... format) {
return new ComponentBuilder(getString(key, format)).color(color).build(); return new ComponentBuilder(getString(key).formatted(format)).color(color).build();
} }
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -9,12 +9,8 @@ package eu.m724.tweaks;
import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import java.util.Map;
public record TweaksConfig( public record TweaksConfig(
boolean metrics, boolean metrics,
boolean debug,
String locale,
boolean worldborderExpand, boolean worldborderExpand,
boolean worldborderHide, boolean worldborderHide,
@ -24,6 +20,7 @@ public record TweaksConfig(
boolean brandShowPing, boolean brandShowPing,
boolean brandShowMspt, boolean brandShowMspt,
boolean doorEnabled,
boolean doorDoubleOpen, boolean doorDoubleOpen,
boolean doorKnocking, boolean doorKnocking,
@ -45,27 +42,16 @@ public record TweaksConfig(
boolean updaterEnabled, boolean updaterEnabled,
boolean hardcoreEnabled, boolean hardcoreEnabled,
double hardcoreChance, float hardcoreChance,
boolean sleepEnabled, boolean sleepEnabled,
boolean sleepInstant, boolean sleepInstant,
double sleepHeal,
boolean authEnabled, boolean authEnabled,
boolean authForce, boolean authForce,
String authDomain, String authDomain
boolean redstoneEnabled,
String redstoneListen,
Map<String, Object> knockbackModifiers,
boolean killswitchEnabled,
String killswitchListen,
boolean swingEnabled
) { ) {
public static final int CONFIG_VERSION = 2; public static final int CONFIG_VERSION = 1;
private static TweaksConfig config; private static TweaksConfig config;
public static TweaksConfig getConfig() { public static TweaksConfig getConfig() {
@ -78,18 +64,15 @@ public record TweaksConfig(
int configVersion = config.getInt("magic number don't modify this", 0); int configVersion = config.getInt("magic number don't modify this", 0);
RuntimeException exception = new RuntimeException("Config version is %d, expected %d".formatted(configVersion, CONFIG_VERSION)); RuntimeException exception = new RuntimeException("Config version is %d, expected %d".formatted(configVersion, CONFIG_VERSION));
if (configVersion == 0) { if (configVersion == 0) {
throw exception; throw exception;
} else if (configVersion < CONFIG_VERSION) {
throw new RuntimeException("Please follow update instructions https://www.spigotmc.org/resources/tweaks724.121057/updates", exception);
} else if (configVersion > CONFIG_VERSION) { } else if (configVersion > CONFIG_VERSION) {
throw new RuntimeException("Please follow update instructions", exception);
} else if (configVersion < CONFIG_VERSION) {
throw new RuntimeException("Did you downgrade the plugin? Remove config.yml and let the plugin re-create it", exception); throw new RuntimeException("Did you downgrade the plugin? Remove config.yml and let the plugin re-create it", exception);
} }
boolean metrics = config.getBoolean("metrics"); boolean metrics = config.getBoolean("metrics");
boolean debug = config.getBoolean("debug", false);
String locale = config.getString("locale", "US");
boolean worldborderExpand = config.getBoolean("worldborder.expand"); boolean worldborderExpand = config.getBoolean("worldborder.expand");
boolean worldborderHide = config.getBoolean("worldborder.hide"); boolean worldborderHide = config.getBoolean("worldborder.hide");
@ -101,6 +84,7 @@ public record TweaksConfig(
boolean doorDoubleOpen = config.getBoolean("doors.doubleOpen"); boolean doorDoubleOpen = config.getBoolean("doors.doubleOpen");
boolean doorKnocking = config.getBoolean("doors.knocking"); boolean doorKnocking = config.getBoolean("doors.knocking");
boolean doorEnabled = doorDoubleOpen || doorKnocking;
String motdSet = config.getString("motd.set"); String motdSet = config.getString("motd.set");
boolean motdEnabled = !(motdSet.equals("false") || motdSet.isBlank()); boolean motdEnabled = !(motdSet.equals("false") || motdSet.isBlank());
@ -120,44 +104,28 @@ public record TweaksConfig(
boolean updaterEnabled = config.getBoolean("updater.enabled"); boolean updaterEnabled = config.getBoolean("updater.enabled");
boolean hardcoreEnabled = config.getBoolean("hardcore.enabled"); boolean hardcoreEnabled = config.getBoolean("hardcore.enabled");
double hardcoreChance = config.getDouble("hardcore.chance"); float hardcoreChance = (float) config.getDouble("hardcore.chance");
boolean sleepEnabled = config.getBoolean("sleep.enabled"); boolean sleepEnabled = config.getBoolean("sleep.enabled");
boolean sleepInstant = config.getBoolean("sleep.instant"); boolean sleepInstant = config.getBoolean("sleep.instant");
double sleepHeal = config.getDouble("sleep.heal");
boolean authEnabled = config.getBoolean("auth.enabled"); boolean authEnabled = config.getBoolean("auth.enabled");
boolean authForce = config.getBoolean("auth.force"); boolean authForce = config.getBoolean("auth.force");
String authHostname = config.getString("auth.domain"); String authHostname = config.getString("auth.domain");
boolean redstoneEnabled = config.getBoolean("retstone.enabled");
String redstoneListen = config.getString("retstone.listen");
// this is processed when initing
Map<String, Object> knockbackModifiers = config.getConfigurationSection("knockback").getValues(false);
boolean killswitchEnabled = config.getBoolean("killswitch.enabled");
String killswitchListen = config.getString("killswitch.listen");
boolean swingEnabled = config.getBoolean("swing.enabled");
TweaksConfig.config = new TweaksConfig( TweaksConfig.config = new TweaksConfig(
metrics, debug, locale, metrics,
worldborderExpand, worldborderHide, worldborderExpand, worldborderHide,
brandEnabled, brandText, brandShowPing, brandShowMspt, brandEnabled, brandText, brandShowPing, brandShowMspt,
doorDoubleOpen, doorKnocking, doorEnabled, doorDoubleOpen, doorKnocking,
motdEnabled, motdSet, motdEnabled, motdSet,
chatEnabled, chatLocalEvents, chatDefaultName, chatRadius, chatEnabled, chatLocalEvents, chatDefaultName, chatRadius,
compassEnabled, compassWidth, compassPrecision, compassEnabled, compassWidth, compassPrecision,
pomodoroEnabled, pomodoroForce, pomodoroEnabled, pomodoroForce,
updaterEnabled, updaterEnabled,
hardcoreEnabled, hardcoreChance, hardcoreEnabled, hardcoreChance,
sleepEnabled, sleepInstant, sleepHeal, sleepEnabled, sleepInstant,
authEnabled, authForce, authHostname, authEnabled, authForce, authHostname
redstoneEnabled, redstoneListen,
knockbackModifiers,
killswitchEnabled, killswitchListen,
swingEnabled
); );
return TweaksConfig.config; return TweaksConfig.config;

View file

@ -1,87 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import org.bukkit.command.CommandExecutor;
import org.bukkit.event.Listener;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Consumer;
public abstract class TweaksModule {
protected abstract void onInit();
void init() {
var name = getClass().getSimpleName();
DebugLogger.finer("Initializing " + name);
long start = System.nanoTime();
this.onInit();
long end = System.nanoTime();
DebugLogger.fine("Initialized %s in %d µs", name, (end - start) / 1000);
}
// TODO not static maybe?
protected TweaksPlugin getPlugin() {
return TweaksPlugin.getInstance();
}
protected TweaksConfig getConfig() {
return TweaksConfig.getConfig();
}
protected void registerEvents(Listener listener) {
DebugLogger.finer("Registered listener: " + listener.getClass().getName());
getPlugin().getServer().getPluginManager().registerEvents(listener, getPlugin());
}
protected void onPacketSend(PacketType packetType, Consumer<PacketEvent> consumer) {
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(getPlugin(), ListenerPriority.NORMAL, packetType) {
@Override
public void onPacketSending(PacketEvent event) {
consumer.accept(event);
}
});
DebugLogger.finer("Registered packet send: " + packetType.name());
}
protected void onPacketReceive(PacketType packetType, Consumer<PacketEvent> consumer) {
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(getPlugin(), ListenerPriority.NORMAL, packetType) {
@Override
public void onPacketReceiving(PacketEvent event) {
consumer.accept(event);
}
});
DebugLogger.finer("Registered packet receive: " + packetType.name());
}
protected void registerCommand(String command, CommandExecutor executor) {
getPlugin().getCommand(command).setExecutor(executor);
DebugLogger.finer("Registered command: " + command);
}
public static <T extends TweaksModule> T init(Class<T> clazz) {
T module;
try {
module = clazz.getDeclaredConstructor().newInstance();
} catch (InstantiationException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
module.init();
return module;
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -7,36 +7,32 @@
package eu.m724.tweaks; package eu.m724.tweaks;
import eu.m724.mstats.MStatsPlugin; import eu.m724.mstats.MStatsPlugin;
import eu.m724.tweaks.alert.AlertModule; import eu.m724.tweaks.alert.AlertManager;
import eu.m724.tweaks.auth.AuthModule; import eu.m724.tweaks.auth.AuthManager;
import eu.m724.tweaks.chat.ChatModule; import eu.m724.tweaks.chat.ChatCommands;
import eu.m724.tweaks.door.DoorKnockModule; import eu.m724.tweaks.chat.ChatManager;
import eu.m724.tweaks.door.DoorOpenModule; import eu.m724.tweaks.door.DoorManager;
import eu.m724.tweaks.full.FullModule; import eu.m724.tweaks.full.FullListener;
import eu.m724.tweaks.hardcore.HardcoreModule; import eu.m724.tweaks.hardcore.HardcoreManager;
import eu.m724.tweaks.killswitch.KillswitchModule; import eu.m724.tweaks.motd.MotdManager;
import eu.m724.tweaks.knockback.KnockbackModule;
import eu.m724.tweaks.motd.MotdModule;
import eu.m724.tweaks.ping.F3NameListener; import eu.m724.tweaks.ping.F3NameListener;
import eu.m724.tweaks.ping.PingChecker; import eu.m724.tweaks.ping.PingChecker;
import eu.m724.tweaks.pomodoro.PomodoroModule; import eu.m724.tweaks.ping.PingCommands;
import eu.m724.tweaks.redstone.RedstoneModule; import eu.m724.tweaks.pomodoro.PomodoroCommands;
import eu.m724.tweaks.sleep.SleepModule; import eu.m724.tweaks.pomodoro.PomodoroManager;
import eu.m724.tweaks.swing.SwingModule; import eu.m724.tweaks.sleep.SleepManager;
import eu.m724.tweaks.updater.UpdaterModule; import eu.m724.tweaks.updater.UpdaterCommands;
import eu.m724.tweaks.worldborder.WorldBorderExpandModule; import eu.m724.tweaks.updater.UpdaterManager;
import eu.m724.tweaks.worldborder.WorldBorderHideModule; import eu.m724.tweaks.worldborder.WorldBorderManager;
import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.logging.Level; import java.util.Objects;
public class TweaksPlugin extends MStatsPlugin { public class TweaksPlugin extends MStatsPlugin {
private static TweaksPlugin INSTANCE;
@Override @Override
public void onEnable() { public void onEnable() {
long start = System.nanoTime(); long start = System.nanoTime();
INSTANCE = this;
if (getServer().getPluginManager().getPlugin("ProtocolLib") == null) { if (getServer().getPluginManager().getPlugin("ProtocolLib") == null) {
getLogger().severe("ProtocolLib is required for this plugin."); getLogger().severe("ProtocolLib is required for this plugin.");
@ -46,106 +42,83 @@ public class TweaksPlugin extends MStatsPlugin {
} }
TweaksConfig config = TweaksConfig.load(this); TweaksConfig config = TweaksConfig.load(this);
new Language(Locale.US); // TODO
getLogger().setLevel(config.debug() ? Level.FINEST : Level.INFO); // whether enabled is handled inside
DebugLogger.logger = getLogger(); new WorldBorderManager().init(this);
if (config.debug()) {
DebugLogger.warning("Debug harms performance");
}
DebugLogger.fine("Language");
new Language(Locale.of(config.locale())); // TODO
DebugLogger.fine(Language.getString("languageNotice", Language.getString("language"), Language.getString("languageEnglish")));
/* start modules */
if (config.worldborderHide()) {
TweaksModule.init(WorldBorderHideModule.class);
}
if (config.worldborderExpand()) {
TweaksModule.init(WorldBorderExpandModule.class);
}
if (config.chatEnabled()) { if (config.chatEnabled()) {
TweaksModule.init(ChatModule.class); ChatManager chatManager = new ChatManager(this);
chatManager.init();
ChatCommands chatCommands = new ChatCommands(chatManager);
Objects.requireNonNull(getCommand("chat")).setExecutor(chatCommands);
Objects.requireNonNull(getCommand("chatmanage")).setExecutor(chatCommands);
} }
if (config.doorKnocking()) { if (config.doorEnabled()) {
TweaksModule.init(DoorKnockModule.class); new DoorManager().init(this);
}
if (config.doorDoubleOpen()) {
TweaksModule.init(DoorOpenModule.class);
} }
if (config.brandEnabled()) { if (config.brandEnabled()) {
DebugLogger.fine("Enabling Brand");
new F3NameListener(this).init(); new F3NameListener(this).init();
} }
DebugLogger.fine("Enabling Ping"); new PingChecker(this).init();
new PingChecker(this).init(getCommand("ping")); Objects.requireNonNull(getCommand("ping")).setExecutor(new PingCommands());
/*if (getServer().getPluginManager().getPlugin("voicechat") != null) {
new MusicPlayer(this).init();
} else {
getLogger().warning("To use voice extensions, install \"Simple Voice Chat\"");
}*/
if (config.motdEnabled()) { if (config.motdEnabled()) {
TweaksModule.init(MotdModule.class); try {
new MotdManager(this).init();
} catch (IOException e) {
getLogger().severe("Failed to initialize MOTD extension");
throw new RuntimeException(e);
}
} }
if (config.pomodoroEnabled()) { if (config.pomodoroEnabled()) {
TweaksModule.init(PomodoroModule.class); new PomodoroManager(this).init();
getCommand("pomodoro").setExecutor(new PomodoroCommands());
} }
if (config.updaterEnabled()) { if (config.updaterEnabled()) {
TweaksModule.init(UpdaterModule.class); try {
new UpdaterManager(this).init();
getCommand("updates").setExecutor(new UpdaterCommands());
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
if (config.hardcoreEnabled()) { if (config.hardcoreEnabled()) {
TweaksModule.init(HardcoreModule.class); new HardcoreManager().init(this);
} }
if (config.sleepEnabled()) { if (config.sleepEnabled()) {
TweaksModule.init(SleepModule.class); new SleepManager().init(this);
} }
if (config.authEnabled()) { if (config.authEnabled()) {
TweaksModule.init(AuthModule.class); new AuthManager(this).init(getCommand("tauth"));
} }
TweaksModule.init(AlertModule.class); new AlertManager(this).init(getCommand("emergencyalert"));
TweaksModule.init(FullModule.class); this.getServer().getPluginManager().registerEvents(new FullListener(), this);
if (config.redstoneEnabled()) { if (config.metrics())
TweaksModule.init(RedstoneModule.class);
}
TweaksModule.init(KnockbackModule.class);
if (config.killswitchEnabled()) {
TweaksModule.init(KillswitchModule.class);
}
if (config.swingEnabled()) {
TweaksModule.init(SwingModule.class);
}
/* end modules */
if (config.metrics()) {
DebugLogger.fine("Enabling Metrics");
mStats(1); mStats(1);
}
DebugLogger.fine("Took %.3f milliseconds".formatted((System.nanoTime() - start) / 1000000.0)); getLogger().info("Took %.3f milliseconds".formatted((System.nanoTime() - start) / 1000000.0));
} }
public boolean hasResource(String resource) { public boolean hasResource(String resource) {
return this.getClassLoader().getResource(resource) != null; return this.getClassLoader().getResource(resource) != null;
} }
public static TweaksPlugin getInstance() {
return INSTANCE;
}
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -20,9 +20,9 @@ public class AlertCommand implements CommandExecutor {
private List<String> pending; private List<String> pending;
private long when; private long when;
private final AlertModule manager; private final AlertManager manager;
public AlertCommand(AlertModule manager) { public AlertCommand(AlertManager manager) {
this.manager = manager; this.manager = manager;
} }
@ -51,7 +51,7 @@ public class AlertCommand implements CommandExecutor {
} else if (args[0].equalsIgnoreCase("confirm")) { } else if (args[0].equalsIgnoreCase("confirm")) {
sender.sendMessage("CONFIRM must be in all caps"); sender.sendMessage("CONFIRM must be in all caps");
} else if (args[0].equalsIgnoreCase("cancel")) { } else if (args[0].equalsIgnoreCase("cancel")) {
if (AlertModule.current != null) { if (AlertManager.current != null) {
manager.stop(); manager.stop();
sender.sendMessage("Cancelled alert"); sender.sendMessage("Cancelled alert");
} else if (pending != null) { } else if (pending != null) {
@ -86,7 +86,7 @@ public class AlertCommand implements CommandExecutor {
if (pending != null) { if (pending != null) {
when = System.currentTimeMillis(); when = System.currentTimeMillis();
if (AlertModule.current != null) { if (AlertManager.current != null) {
sender.sendMessage("Broadcasting a new alert will cancel the currently active one"); sender.sendMessage("Broadcasting a new alert will cancel the currently active one");
} }
sender.sendMessage("Please confirm broadcast with /emergencyalert CONFIRM within 15 seconds"); sender.sendMessage("Please confirm broadcast with /emergencyalert CONFIRM within 15 seconds");

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.alert;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import eu.m724.tweaks.TweaksPlugin;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.util.HashMap;
import java.util.Map;
public class AlertManager {
private final TweaksPlugin plugin;
private BukkitTask notifyTask;
static Alert current;
static Map<Player, Integer> pages = new HashMap<>();
public AlertManager(TweaksPlugin plugin) {
this.plugin = plugin;
}
public void init(PluginCommand command) {
command.setExecutor(new AlertCommand(this));
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Client.ENCHANT_ITEM
) {
@Override
public void onPacketReceiving(PacketEvent event) {
if (current == null) return;
if (!current.isOpen(event.getPlayer())) return;
PacketContainer packet = event.getPacket();
int windowId, buttonId;
windowId = packet.getIntegers().read(0);
buttonId = packet.getIntegers().read(1);
var page = pages.getOrDefault(event.getPlayer(),1);
if (buttonId == 1) { // prev page
page--;
} else if (buttonId == 2) { // nextc page
page++;
} else {
return;
}
pages.put(event.getPlayer(), page);
var npacket = new PacketContainer(PacketType.Play.Server.WINDOW_DATA);
npacket.getIntegers().write(0, windowId);
npacket.getIntegers().write(1, 0);
npacket.getIntegers().write(2, page);
ProtocolLibrary.getProtocolManager().sendServerPacket(event.getPlayer(), npacket);
}
});
}
public Alert start(String... content) {
stop();
current = new Alert(content);
notifyTask = new AlertRunnable(current, v -> this.stop()).runTaskTimer(plugin, 0, 10);
return current;
}
public void stop() {
if (current == null) return;
for (Player player : plugin.getServer().getOnlinePlayers()) {
if (current.isOpen(player))
player.closeInventory();
}
pages.clear();
notifyTask.cancel();
current = null;
}
}

View file

@ -1,74 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.alert;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.util.HashMap;
import java.util.Map;
public class AlertModule extends TweaksModule {
private BukkitTask notifyTask;
static Alert current;
static Map<Player, Integer> pages = new HashMap<>();
@Override
protected void onInit() {
registerCommand("emergencyalert", new AlertCommand(this));
onPacketReceive(PacketType.Play.Client.ENCHANT_ITEM, (event) -> {
if (current == null) return;
if (!current.isOpen(event.getPlayer())) return;
PacketContainer packet = event.getPacket();
int windowId, buttonId;
windowId = packet.getIntegers().read(0);
buttonId = packet.getIntegers().read(1);
var page = pages.getOrDefault(event.getPlayer(),1);
if (buttonId == 1) { // prev page
page--;
} else if (buttonId == 2) { // nextc page
page++;
} else {
return;
}
pages.put(event.getPlayer(), page);
var npacket = new PacketContainer(PacketType.Play.Server.WINDOW_DATA);
npacket.getIntegers().write(0, windowId);
npacket.getIntegers().write(1, 0);
npacket.getIntegers().write(2, page);
ProtocolLibrary.getProtocolManager().sendServerPacket(event.getPlayer(), npacket);
});
}
public Alert start(String... content) {
stop();
current = new Alert(content);
notifyTask = new AlertRunnable(current, v -> this.stop()).runTaskTimer(getPlugin(), 0, 10);
return current;
}
public void stop() {
if (current == null) return;
for (Player player : getPlugin().getServer().getOnlinePlayers()) {
if (current.isOpen(player))
player.closeInventory();
}
pages.clear();
notifyTask.cancel();
current = null;
}
}

View file

@ -6,7 +6,6 @@
package eu.m724.tweaks.auth; package eu.m724.tweaks.auth;
import eu.m724.tweaks.Language;
import eu.m724.tweaks.TweaksConfig; import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
@ -51,7 +50,7 @@ public class AuthCommands implements CommandExecutor {
.underlined(true) .underlined(true)
.color(ChatColor.GRAY) .color(ChatColor.GRAY)
.event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hostname)) .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hostname))
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.getString("clickToCopy")))) .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to copy")))
.build(); .build();
sender.spigot().sendMessage(component); sender.spigot().sendMessage(component);
} else { } else {

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.auth;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin;
public class AuthManager {
private final AuthStorage authStorage;
private final Plugin plugin;
public AuthManager(Plugin plugin) {
this.plugin = plugin;
this.authStorage = new AuthStorage(plugin);
}
public void init(PluginCommand command) {
plugin.getServer().getPluginManager().registerEvents(new AuthListener(authStorage), plugin);
command.setExecutor(new AuthCommands(authStorage));
}
}

View file

@ -1,19 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.auth;
import eu.m724.tweaks.TweaksModule;
public class AuthModule extends TweaksModule {
@Override
protected void onInit() {
var authStorage = new AuthStorage(getPlugin());
registerEvents(new AuthListener(authStorage));
registerCommand("tauth", new AuthCommands(authStorage));
}
}

View file

@ -19,11 +19,12 @@ public class AuthStorage {
private final File keysDirectory; private final File keysDirectory;
AuthStorage(Plugin plugin) { AuthStorage(Plugin plugin) {
File directory = new File(plugin.getDataFolder(), "storage/auth"); File directory = new File(plugin.getDataFolder(), "auth storage");
this.playersDirectory = new File(directory, "players"); this.playersDirectory = new File(directory, "players");
this.keysDirectory = new File(directory, "keys"); this.keysDirectory = new File(directory, "keys");
keysDirectory.mkdirs(); directory.mkdir();
keysDirectory.mkdir();
playersDirectory.mkdir(); playersDirectory.mkdir();
} }

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -21,9 +21,9 @@ import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ChatCommands implements CommandExecutor { public class ChatCommands implements CommandExecutor {
private final ChatModule manager; private final ChatManager manager;
public ChatCommands(ChatModule manager) { public ChatCommands(ChatManager manager) {
this.manager = manager; this.manager = manager;
} }
@ -94,9 +94,9 @@ public class ChatCommands implements CommandExecutor {
ChatRoom newRoom = manager.createChatRoom(argument, null, player); ChatRoom newRoom = manager.createChatRoom(argument, null, player);
sender.sendMessage("Created a chat room. Join it: /c " + newRoom.id); sender.sendMessage("Created a chat room. Join it: /c " + newRoom.id);
sender.sendMessage("You might also want to protect it with a password: /cm setpassword"); sender.sendMessage("You might also want to protect it with a password: /cm setpassword");
} catch (ChatModule.InvalidIdException e) { } catch (ChatManager.InvalidIdException e) {
sender.sendMessage("ID is invalid: " + e.getMessage()); sender.sendMessage("ID is invalid: " + e.getMessage());
} catch (ChatModule.ChatRoomExistsException e) { } catch (ChatManager.ChatRoomExistsException e) {
sender.sendMessage("Room %s already exists".formatted(argument)); sender.sendMessage("Room %s already exists".formatted(argument));
} catch (IOException e) { } catch (IOException e) {
sender.sendMessage("Failed to create room"); sender.sendMessage("Failed to create room");

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -25,14 +25,13 @@ import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
public class ChatListener implements Listener { public class ChatListener implements Listener {
private final ChatModule chatModule; private final ChatManager chatManager;
private final String defaultRoom = TweaksConfig.getConfig().chatDefaultName(); private final String defaultRoom = TweaksConfig.getConfig().chatDefaultName();
private final boolean localEvents = TweaksConfig.getConfig().chatLocalEvents(); private final boolean localEvents = TweaksConfig.getConfig().chatLocalEvents();
private final int radius; private final int radius;
public ChatListener(ChatModule chatModule) { public ChatListener(ChatManager chatManager) {
this.chatModule = chatModule; this.chatManager = chatManager;
if (TweaksConfig.getConfig().chatRadius() < 0) { if (TweaksConfig.getConfig().chatRadius() < 0) {
radius = 0; radius = 0;
} else { } else {
@ -53,7 +52,7 @@ public class ChatListener implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
ChatRoom chatRoom = chatModule.getPlayerChatRoom(player); ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
if (localEvents) { if (localEvents) {
var cb = new ComponentBuilder() var cb = new ComponentBuilder()
@ -78,7 +77,7 @@ public class ChatListener implements Listener {
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
ChatRoom chatRoom = chatModule.removePlayer(player); ChatRoom chatRoom = chatManager.removePlayer(player);
if (localEvents) { if (localEvents) {
var cb = new ComponentBuilder() var cb = new ComponentBuilder()
@ -104,7 +103,7 @@ public class ChatListener implements Listener {
public void onPlayerDeath(PlayerDeathEvent event) { public void onPlayerDeath(PlayerDeathEvent event) {
if (localEvents) { if (localEvents) {
Player player = event.getEntity(); Player player = event.getEntity();
ChatRoom chatRoom = chatModule.getPlayerChatRoom(player); ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
// would be easier on Paper but this is not Paper // would be easier on Paper but this is not Paper
BaseComponent deathMessage = ComponentSerializer.deserialize(Component.Serializer.toJson(((CraftPlayer)player).getHandle().getCombatTracker().getDeathMessage(), CraftRegistry.getMinecraftRegistry())); BaseComponent deathMessage = ComponentSerializer.deserialize(Component.Serializer.toJson(((CraftPlayer)player).getHandle().getCombatTracker().getDeathMessage(), CraftRegistry.getMinecraftRegistry()));
@ -125,7 +124,7 @@ public class ChatListener implements Listener {
// broadcast to killer if available // broadcast to killer if available
if (player.getLastDamageCause().getDamageSource().getCausingEntity() instanceof Player killer) { if (player.getLastDamageCause().getDamageSource().getCausingEntity() instanceof Player killer) {
ChatRoom chatRoom2 = chatModule.getPlayerChatRoom(killer); ChatRoom chatRoom2 = chatManager.getPlayerChatRoom(killer);
if (chatRoom != chatRoom2) { if (chatRoom != chatRoom2) {
if (proximityFor(chatRoom)) { if (proximityFor(chatRoom)) {
chatRoom2.broadcastNear(killer.getLocation(), radius, component); chatRoom2.broadcastNear(killer.getLocation(), radius, component);
@ -143,7 +142,7 @@ public class ChatListener implements Listener {
@EventHandler @EventHandler
public void onAsyncPlayerChat(AsyncPlayerChatEvent event) { public void onAsyncPlayerChat(AsyncPlayerChatEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
ChatRoom chatRoom = chatModule.getPlayerChatRoom(player); ChatRoom chatRoom = chatManager.getPlayerChatRoom(player);
String message = event.getMessage(); String message = event.getMessage();
var component = new ComponentBuilder() var component = new ComponentBuilder()

View file

@ -1,43 +1,45 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.chat; package eu.m724.tweaks.chat;
import eu.m724.tweaks.TweaksModule; import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
public class ChatModule extends TweaksModule { public class ChatManager {
private final NamespacedKey chatRoomKey = new NamespacedKey(getPlugin(), "chatRoom"); private final Plugin plugin;
private final String defaultRoom = getConfig().chatDefaultName(); private final NamespacedKey chatRoomKey;
private final String defaultRoom;
private final Map<Player, ChatRoom> playerMap = new HashMap<>(); private final Map<Player, ChatRoom> playerMap = new HashMap<>();
private final Map<String, ChatRoom> roomIdMap = new HashMap<>(); private final Map<String, ChatRoom> roomIdMap = new HashMap<>();
public ChatManager(Plugin plugin) {
this.plugin = plugin;
this.chatRoomKey = new NamespacedKey(plugin, "chatRoom");
this.defaultRoom = TweaksConfig.getConfig().chatDefaultName();
}
@Override public void init() {
protected void onInit() { if (plugin.getServer().isEnforcingSecureProfiles()) {
if (getPlugin().getServer().isEnforcingSecureProfiles()) {
throw new RuntimeException("Please disable enforce-secure-profile in server.properties to use chatrooms"); throw new RuntimeException("Please disable enforce-secure-profile in server.properties to use chatrooms");
} }
getById(defaultRoom); getById(defaultRoom);
registerEvents(new ChatListener(this)); plugin.getServer().getPluginManager().registerEvents(new ChatListener(this), plugin);
var chatCommands = new ChatCommands(this);
registerCommand("chat", chatCommands);
registerCommand("chatmanage", chatCommands);
} }
/** /**
@ -67,7 +69,7 @@ public class ChatModule extends TweaksModule {
if (id.equals(defaultRoom)) { if (id.equals(defaultRoom)) {
chatRoom = new ChatRoom(defaultRoom, null, null); chatRoom = new ChatRoom(defaultRoom, null, null);
} else { } else {
chatRoom = ChatRoomLoader.load(id); chatRoom = ChatRoomLoader.load(plugin, id);
} }
roomIdMap.put(id, chatRoom); roomIdMap.put(id, chatRoom);
} }
@ -164,17 +166,17 @@ public class ChatModule extends TweaksModule {
throw new ChatRoomExistsException(); throw new ChatRoomExistsException();
ChatRoom chatRoom = new ChatRoom(id, password, owner); ChatRoom chatRoom = new ChatRoom(id, password, owner);
ChatRoomLoader.save(chatRoom); ChatRoomLoader.save(plugin, chatRoom);
return chatRoom; return chatRoom;
} }
void saveChatRoom(ChatRoom chatRoom) throws IOException { void saveChatRoom(ChatRoom chatRoom) throws IOException {
ChatRoomLoader.save(chatRoom); ChatRoomLoader.save(plugin, chatRoom);
} }
public void deleteChatRoom(ChatRoom chatRoom) { public void deleteChatRoom(ChatRoom chatRoom) {
roomIdMap.remove(chatRoom.id); roomIdMap.remove(chatRoom.id);
ChatRoomLoader.getFile(chatRoom.id).delete(); ChatRoomLoader.getFile(plugin, chatRoom.id).delete();
chatRoom.players.forEach(player -> setPlayerChatRoom(getById(defaultRoom), player)); chatRoom.players.forEach(player -> setPlayerChatRoom(getById(defaultRoom), player));
} }

View file

@ -13,25 +13,23 @@ import org.bukkit.plugin.Plugin;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID; import java.util.UUID;
public class ChatRoomLoader { public class ChatRoomLoader {
private static File chatRoomsDir;
static void init(Plugin plugin) {
chatRoomsDir = new File(plugin.getDataFolder(), "storage/rooms");
chatRoomsDir.mkdirs();
}
/** /**
* Get the file of persistent storage of a chat room * Get the file of persistent storage of a chat room
* @return the file or null if ID is invalid * @return the file or null if ID is invalid
*/ */
static File getFile(String id) { static File getFile(Plugin plugin, String id) {
Path chatRoomsPath = Paths.get(plugin.getDataFolder().getPath(), "rooms");
chatRoomsPath.toFile().mkdirs();
if (validateId(id) != 0) if (validateId(id) != 0)
throw new RuntimeException("Invalid id: " + id); throw new RuntimeException("Invalid id: " + id);
return new File(chatRoomsDir, id + ".yml"); return Paths.get(chatRoomsPath.toFile().getPath(), id + ".yml").toFile();
} }
/** /**
@ -64,8 +62,8 @@ public class ChatRoomLoader {
* @param id the id of the chat room * @param id the id of the chat room
* @return the chat room or null if no such chat room * @return the chat room or null if no such chat room
*/ */
static ChatRoom load(String id) { static ChatRoom load(Plugin plugin, String id) {
File chatRoomFile = getFile(id); File chatRoomFile = getFile(plugin, id);
if (!chatRoomFile.exists()) return null; if (!chatRoomFile.exists()) return null;
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(chatRoomFile); YamlConfiguration configuration = YamlConfiguration.loadConfiguration(chatRoomFile);
@ -91,15 +89,14 @@ public class ChatRoomLoader {
* *
* @throws IOException if saving failed * @throws IOException if saving failed
*/ */
static void save(ChatRoom chatRoom) throws IOException { static void save(Plugin plugin, ChatRoom chatRoom) throws IOException {
YamlConfiguration configuration = new YamlConfiguration(); YamlConfiguration configuration = new YamlConfiguration();
configuration.set("password", chatRoom.password); configuration.set("password", chatRoom.password);
configuration.set("color", chatRoom.color.getName()); configuration.set("color", chatRoom.color.getName());
// TODO consider just making this str to make it easier
configuration.set("owner.msb", chatRoom.owner.getUniqueId().getMostSignificantBits()); configuration.set("owner.msb", chatRoom.owner.getUniqueId().getMostSignificantBits());
configuration.set("owner.lsb", chatRoom.owner.getUniqueId().getLeastSignificantBits()); configuration.set("owner.lsb", chatRoom.owner.getUniqueId().getLeastSignificantBits());
File chatRoomFile = getFile(chatRoom.id); File chatRoomFile = getFile(plugin, chatRoom.id);
configuration.save(chatRoomFile); configuration.save(chatRoomFile);
} }
} }

View file

@ -1,12 +1,12 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.compass; package eu.m724.tweaks.compass;
import eu.m724.tweaks.TweaksModule; import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
@ -25,9 +25,9 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
// TODO dimension check // TODO dimension check
public class CompassModule extends TweaksModule implements Listener { public class CompassListener implements Listener {
private final int precision = getConfig().compassPrecision(); // degrees every point private final int precision = TweaksConfig.getConfig().compassPrecision(); // degrees every point
private final int width = getConfig().compassWidth(); // points left to right private final int width = TweaksConfig.getConfig().compassWidth(); // points left to right
private final Map<Integer, String> points = Map.of( private final Map<Integer, String> points = Map.of(
0, ChatColor.DARK_GRAY + "S", 0, ChatColor.DARK_GRAY + "S",
@ -36,11 +36,6 @@ public class CompassModule extends TweaksModule implements Listener {
270, ChatColor.DARK_GRAY + "E" 270, ChatColor.DARK_GRAY + "E"
); );
@Override
protected void onInit() {
registerEvents(this);
}
@EventHandler @EventHandler
public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) { public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent event) {
if (event.getMainHandItem().getType() == Material.COMPASS || event.getOffHandItem().getType() == Material.COMPASS) { if (event.getMainHandItem().getType() == Material.COMPASS || event.getOffHandItem().getType() == Material.COMPASS) {

View file

@ -0,0 +1,21 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.compass;
import org.bukkit.plugin.Plugin;
public class CompassManager {
private final Plugin plugin;
public CompassManager(Plugin plugin) {
this.plugin = plugin;
}
public void init() {
plugin.getServer().getPluginManager().registerEvents(new CompassListener(), plugin);
}
}

View file

@ -1,12 +1,11 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.door; package eu.m724.tweaks.door;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Sound; import org.bukkit.Sound;
@ -23,12 +22,7 @@ import org.bukkit.util.RayTraceResult;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
public class DoorKnockModule extends TweaksModule implements Listener { public class DoorKnockListener implements Listener {
@Override
protected void onInit() {
registerEvents(this);
}
@EventHandler @EventHandler
public void onBlockDamageAbort(BlockDamageAbortEvent event) { public void onBlockDamageAbort(BlockDamageAbortEvent event) {
Block block = event.getBlock(); Block block = event.getBlock();

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.door;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.plugin.Plugin;
public class DoorManager {
public void init(Plugin plugin) {
if (TweaksConfig.getConfig().doorKnocking()) {
plugin.getServer().getPluginManager().registerEvents(new DoorKnockListener(), plugin);
}
if (TweaksConfig.getConfig().doorDoubleOpen()) {
plugin.getServer().getPluginManager().registerEvents(new DoorOpenListener(), plugin);
}
}
}

View file

@ -1,12 +1,11 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.door; package eu.m724.tweaks.door;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
@ -16,11 +15,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
public class DoorOpenModule extends TweaksModule implements Listener { public class DoorOpenListener implements Listener {
@Override
protected void onInit() {
registerEvents(this);
}
@EventHandler @EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
@ -76,4 +71,5 @@ public class DoorOpenModule extends TweaksModule implements Listener {
location.getBlock().setBlockData(nextDoor); location.getBlock().setBlockData(nextDoor);
} }
} }
} }

View file

@ -1,22 +1,16 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.full; package eu.m724.tweaks.full;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
public class FullModule extends TweaksModule implements Listener { public class FullListener implements Listener {
@Override
protected void onInit() {
registerEvents(this);
}
@EventHandler @EventHandler
public void onPlayerLogin(PlayerLoginEvent event) { public void onPlayerLogin(PlayerLoginEvent event) {
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL && event.getPlayer().hasPermission("tweaks724.bypass-full")) { if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL && event.getPlayer().hasPermission("tweaks724.bypass-full")) {

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.hardcore;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.*;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.plugin.Plugin;
public class HardcoreManager {
private final float chance = TweaksConfig.getConfig().hardcoreChance();
public void init(Plugin plugin) {
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.LOGIN
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
int entityId = packet.getIntegers().read(0);
if (chance > ((48271 * entityId) % 65537) / 65537f) // gotta be fast
// the "is hardcore" boolean https://wiki.vg/Protocol#Login_.28play.29
packet.getBooleans().write(0, true);
}
});
}
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.hardcore;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import net.minecraft.world.level.levelgen.RandomSupport;
import net.minecraft.world.level.levelgen.Xoroshiro128PlusPlus;
import java.math.BigDecimal;
import java.math.BigInteger;
// how we do it is much faster than any Random
public class HardcoreModule extends TweaksModule {
private final Xoroshiro128PlusPlus rng;
private final long chanceLong;
public HardcoreModule() {
this.rng = new Xoroshiro128PlusPlus(
RandomSupport.generateUniqueSeed(),
RandomSupport.generateUniqueSeed()
);
this.chanceLong = BigInteger.valueOf(Long.MIN_VALUE)
.add(
new BigDecimal(
BigInteger.valueOf(Long.MAX_VALUE).subtract(BigInteger.valueOf(Long.MIN_VALUE))
).multiply(
BigDecimal.valueOf(getConfig().hardcoreChance())
).toBigInteger()
).longValue();
}
@Override
protected void onInit() {
DebugLogger.fine("Chance long: " + chanceLong);
onPacketSend(PacketType.Play.Server.LOGIN, (event) -> {
PacketContainer packet = event.getPacket();
if (rng.nextLong() < chanceLong)
// the "is hardcore" boolean https://wiki.vg/Protocol#Login_.28play.29
packet.getBooleans().write(0, true);
});
}
}

View file

@ -1,118 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.killswitch;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
public class KillswitchModule extends TweaksModule implements CommandExecutor, HttpHandler {
private final Ratelimit ratelimit = new Ratelimit();
private byte[] secret;
private String secretEncoded;
private void loadKey(File file) {
if (file.exists()) {
try {
this.secret = Files.readAllBytes(file.toPath());
} catch (IOException e) {
throw new RuntimeException("Reading killswitch key", e);
}
DebugLogger.fine("Loaded key");
} else {
byte[] buf = new byte[16];
try {
SecureRandom.getInstanceStrong().nextBytes(buf);
Files.write(file.toPath(), buf);
} catch (IOException | NoSuchAlgorithmException e) {
throw new RuntimeException("Generating killswitch key", e);
}
this.secret = buf;
DebugLogger.info("Killswitch key generated and saved to " + file.getPath());
}
this.secretEncoded = Base64.getEncoder().encodeToString(secret);
}
@Override
protected void onInit() {
registerCommand("servkill", this);
if (getConfig().killswitchListen() != null) {
loadKey(new File(getPlugin().getDataFolder(), "storage/killswitch key"));
ratelimit.runTaskTimerAsynchronously(getPlugin(), 0, 20 * 300);
var listenAddress = getConfig().killswitchListen().split(":");
InetSocketAddress bindAddress;
if (listenAddress.length == 1) {
bindAddress = new InetSocketAddress(Integer.parseInt(listenAddress[0]));
} else {
bindAddress = new InetSocketAddress(listenAddress[0], Integer.parseInt(listenAddress[1]));
}
try {
HttpServer server = HttpServer.create(bindAddress, 0);
server.createContext("/", this);
server.setExecutor(null);
server.start();
DebugLogger.fine("server started");
} catch (IOException e) {
throw new RuntimeException("Starting HTTP server", e);
}
}
}
private void kill() {
Runtime.getRuntime().halt(0);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
kill();
return true;
}
@Override
public void handle(HttpExchange exchange) throws IOException {
exchange.sendResponseHeaders(404, -1);
var path = exchange.getRequestURI().getPath();
if (!path.startsWith("/key/")) return;
var address = exchange.getRemoteAddress().getAddress();
if (!ratelimit.submitRequest(address)) {
DebugLogger.fine(address + " is ratelimited");
return;
}
var key = path.substring(5);
if (key.equals(secretEncoded)) {
DebugLogger.fine("Got a request with valid key");
kill();
} else {
DebugLogger.fine("Got a request with invalid key");
}
}
}

View file

@ -1,26 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.killswitch;
import org.bukkit.scheduler.BukkitRunnable;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
public class Ratelimit extends BukkitRunnable {
private Set<InetAddress> requests = new HashSet<>();
boolean submitRequest(InetAddress address) {
return requests.add(address);
}
@Override
public void run() {
requests = new HashSet<>();
}
}

View file

@ -1,69 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.knockback;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.entity.EntityType;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityKnockbackByEntityEvent;
import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.Map;
public class KnockbackModule extends TweaksModule implements Listener {
private final Map<EntityType, Vector> modifiers = new HashMap<>();
@Override
protected void onInit() {
getConfig().knockbackModifiers().forEach((k, v) -> {
EntityType type;
double mod;
String line = "(%s: %s)".formatted(k, v);
if (v instanceof Double d) {
mod = d;
} else if (v instanceof Integer i) {
mod = i;
} else {
DebugLogger.warning("In " + line + " the value is not a number ");
return;
}
try {
type = EntityType.valueOf(k);
} catch (IllegalArgumentException e) {
DebugLogger.warning("In" + line + " the key is not a valid entity type");
DebugLogger.warning("Valid entity types: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html");
return;
}
if (mod == 1) return;
modifiers.put(type, new Vector(mod, mod >= 1 ? mod : 1, mod)); // don't touch vertical
});
if (!modifiers.isEmpty()) {
registerEvents(this);
try {
Class.forName("com.destroystokyo.paper.event.entity.EntityKnockbackByEntityEvent");
DebugLogger.warning("Ignore that. Server performance will NOT be affected.");
} catch (ClassNotFoundException ignored) { }
}
}
@EventHandler
public void onEntityKnockbackByEntity(EntityKnockbackByEntityEvent event) {
var modifier = modifiers.get(event.getSourceEntity().getType());
if (modifier != null) {
event.setFinalKnockback(event.getKnockback().multiply(modifier));
}
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.motd;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.gson.JsonElement;
import eu.m724.tweaks.TweaksConfig;
import eu.m724.tweaks.TweaksPlugin;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.status.ServerStatus;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
public class MotdManager {
private final TweaksPlugin plugin;
private Component[] motds;
public MotdManager(TweaksPlugin plugin) {
this.plugin = plugin;
}
public void init() throws IOException {
// TODO adding more MOTD features would require checking whether to enable set
String motdSetName = TweaksConfig.getConfig().motdSet();
String motdSetPath = "motd sets/" + motdSetName + ".txt";
File motdSetsFile = new File(plugin.getDataFolder(), motdSetPath);
// create "motd sets" directory
motdSetsFile.getParentFile().mkdirs();
// if this is a builtin set
if (!motdSetsFile.exists() && plugin.hasResource(motdSetPath))
plugin.saveResource(motdSetPath, false);
if (!motdSetsFile.exists()) {
throw new RuntimeException("MOTD set \"%s\" doesn't exist".formatted(motdSetName));
}
String fileContent = Files.readString(motdSetsFile.toPath());
// MOTDs are split with an empty line
motds = Arrays.stream(fileContent.split("\n\n"))
.map(s -> {
JsonElement json = ComponentSerializer.toJson(TextComponent.fromLegacy(s.strip()));
return Component.Serializer.fromJson(json, RegistryAccess.EMPTY);
})
.toArray(Component[]::new);
registerListener(plugin);
}
private void registerListener(Plugin plugin) {
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Status.Server.SERVER_INFO
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
Component motd = motds[ThreadLocalRandom.current().nextInt(motds.length)];
ServerStatus serverStatus = (ServerStatus) packet.getStructures().read(0).getHandle();
/* this:
* removes server mod prefix (Paper, Spigot, any brand)
* hides players
*/
ServerStatus newStatus = new ServerStatus(
motd,
Optional.empty(),
Optional.of(new ServerStatus.Version(
SharedConstants.getCurrentVersion().getName(),
SharedConstants.getProtocolVersion()
)),
serverStatus.favicon(),
false
);
packet.getStructures().write(0, new InternalStructure(newStatus, new StructureModifier<>(ServerStatus.class)));
}
});
}
}

View file

@ -1,107 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.motd;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksConfig;
import eu.m724.tweaks.TweaksModule;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.minecraft.SharedConstants;
import net.minecraft.core.RegistryAccess;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.status.ServerStatus;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
public class MotdModule extends TweaksModule {
private Component[] motds;
@Override
protected void onInit() {
// TODO adding more MOTD features would require checking whether to enable set
String motdSetName = TweaksConfig.getConfig().motdSet();
String motdSetPath = "motd sets/" + motdSetName + ".txt";
File motdSetsFile = new File(getPlugin().getDataFolder(), motdSetPath);
// create "motd sets" directory
motdSetsFile.getParentFile().mkdirs();
// if this is a builtin set
if (!motdSetsFile.exists() && getPlugin().hasResource(motdSetPath))
getPlugin().saveResource(motdSetPath, false);
if (!motdSetsFile.exists()) {
throw new RuntimeException("MOTD set \"%s\" doesn't exist".formatted(motdSetName));
}
String fileContent;
try {
fileContent = Files.readString(motdSetsFile.toPath());
} catch (IOException e) {
throw new RuntimeException("Reading motd set", e);
}
// MOTDs are split with an empty line
motds = Arrays.stream(fileContent.split("\n\n"))
.map(entry -> {
entry = entry.strip();
JsonElement json = null;
try {
json = JsonParser.parseString(entry);
DebugLogger.finer("JSON line: %s...", entry.substring(0, Math.min(entry.length(), 10)));
} catch (JsonParseException e) {
DebugLogger.finer("JSON line: %s...", entry.substring(0, Math.min(entry.length(), 10)));
}
if (json == null) {
json = ComponentSerializer.toJson(TextComponent.fromLegacy(entry));
}
return Component.Serializer.fromJson(json, RegistryAccess.EMPTY);
})
.toArray(Component[]::new);
onPacketSend(PacketType.Status.Server.SERVER_INFO, (event) -> {
PacketContainer packet = event.getPacket();
Component motd = motds[ThreadLocalRandom.current().nextInt(motds.length)];
ServerStatus serverStatus = (ServerStatus) packet.getStructures().read(0).getHandle();
/* this:
* removes server mod prefix (Paper, Spigot, any brand)
* hides players
*/
ServerStatus newStatus = new ServerStatus(
motd,
Optional.empty(),
Optional.of(new ServerStatus.Version(
SharedConstants.getCurrentVersion().getName(),
SharedConstants.getProtocolVersion()
)),
serverStatus.favicon(),
false
);
packet.getStructures().write(0, new InternalStructure(newStatus, new StructureModifier<>(ServerStatus.class)));
});
}
}

View file

@ -1,12 +1,11 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.ping; package eu.m724.tweaks.ping;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
public class PingChecker { public class PingChecker {
@ -16,10 +15,8 @@ public class PingChecker {
this.plugin = plugin; this.plugin = plugin;
} }
public void init(PluginCommand pingCommand) { public void init() {
new KeepAlivePingChecker(plugin).start(); new KeepAlivePingChecker(plugin).start();
new MsptChecker().init(plugin); // TODO should this be here new MsptChecker().init(plugin); // TODO should this be here
pingCommand.setExecutor(new PingCommands());
} }
} }

View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.pomodoro;
import org.bukkit.plugin.Plugin;
public class PomodoroManager {
private final Plugin plugin;
public PomodoroManager(Plugin plugin) {
this.plugin = plugin;
}
public void init() {
plugin.getServer().getPluginManager().registerEvents(new PomodoroListener(), plugin);
new PomodoroRunnable(plugin).runTaskTimerAsynchronously(plugin, 0, 20L);
}
}

View file

@ -1,19 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.pomodoro;
import eu.m724.tweaks.TweaksModule;
public class PomodoroModule extends TweaksModule {
@Override
protected void onInit() {
registerEvents(new PomodoroListener());
new PomodoroRunnable(getPlugin()).runTaskTimerAsynchronously(getPlugin(), 0, 20L);
registerCommand("pomodoro", new PomodoroCommands());
}
}

View file

@ -6,6 +6,7 @@
package eu.m724.tweaks.pomodoro; package eu.m724.tweaks.pomodoro;
import eu.m724.tweaks.Language;
import eu.m724.tweaks.TweaksConfig; import eu.m724.tweaks.TweaksConfig;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -36,8 +37,7 @@ public class PomodoroRunnable extends BukkitRunnable {
player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_FALL, 1.0f, 0.5f); player.playSound(player.getLocation(), Sound.BLOCK_ANVIL_FALL, 1.0f, 0.5f);
if (remaining < -60 && force) { if (remaining < -60 && force) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
pomodoro.next(); player.kickPlayer(Language.getString("pomodoroEndKick"));
player.kickPlayer(Pomodoros.formatTimer(pomodoro, pomodoro.getRemainingSeconds(now)).toLegacyText());
}); });
} }
} }

View file

@ -30,7 +30,7 @@ public class Pomodoros {
return timers.remove(player.getUniqueId()) != null; return timers.remove(player.getUniqueId()) != null;
} }
static BaseComponent formatTimer(PlayerPomodoro pomodoro, long remaining) { static BaseComponent[] formatTimer(PlayerPomodoro pomodoro, long remaining) {
ComponentBuilder builder = new ComponentBuilder(); ComponentBuilder builder = new ComponentBuilder();
if (pomodoro.isBreak()) { if (pomodoro.isBreak()) {
@ -66,6 +66,6 @@ public class Pomodoros {
builder.append(" o").color(color); builder.append(" o").color(color);
} }
return builder.build(); return builder.create();
} }
} }

View file

@ -1,58 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.Language;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
public class GatewayItem {
private final NamespacedKey gatewayKey;
public GatewayItem(Plugin plugin) {
var start = System.nanoTime();
this.gatewayKey = new NamespacedKey(plugin, "gateway");
var recipe = new ShapelessRecipe(gatewayKey, itemStack());
// this takes a long time for some reason. The first one especially
// do this somewhere off measure to JIT and skew the measurement: new RecipeChoice.MaterialChoice(Material.DIRT);
recipe.addIngredient(Material.NETHER_STAR);
recipe.addIngredient(Material.ENDER_CHEST);
recipe.addIngredient(Material.CHORUS_FLOWER);
recipe.addIngredient(Material.DAYLIGHT_DETECTOR);
plugin.getServer().addRecipe(recipe);
DebugLogger.finer("Adding the recipe took %d ms, which is a long time", (System.nanoTime() - start) / 1000000);
}
public ItemStack itemStack() {
var itemStack = new ItemStack(Material.DAYLIGHT_DETECTOR);
var meta = itemStack.getItemMeta();
meta.setItemName(Language.getString("redstoneGatewayItem"));
meta.getPersistentDataContainer().set(gatewayKey, PersistentDataType.BOOLEAN, true);
meta.setEnchantmentGlintOverride(true);
itemStack.setItemMeta(meta);
return itemStack;
}
public boolean isGateway(ItemStack itemStack) {
var meta = itemStack.getItemMeta();
if (meta == null) return false;
var value = meta.getPersistentDataContainer().get(gatewayKey, PersistentDataType.BOOLEAN);
return value != null;
}
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class RedstoneCommands implements CommandExecutor {
private final GatewayItem gatewayItem;
public RedstoneCommands(GatewayItem gatewayItem) {
this.gatewayItem = gatewayItem;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length > 0) {
if (args[0].equals("give")) {
Player player = null;
if (args.length > 1) {
player = Bukkit.getPlayerExact(args[1]);
if (player == null) {
sender.sendMessage("No player named " + args[1]);
return true;
}
} else {
if (sender instanceof Player) {
player = (Player) sender;
} else {
sender.sendMessage("Specify a player to give to, or be a player");
}
}
var itemStack = gatewayItem.itemStack();
player.getInventory().addItem(itemStack);
sender.sendMessage("Given to " + player.getName());
}
} else {
sender.sendMessage("Argument needed");
}
return true;
}
}

View file

@ -1,179 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import eu.m724.tweaks.DebugLogger;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.data.AnaloguePowerable;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
public class RedstoneGateways {
private final Plugin plugin;
private final BiMap<Integer, Location> gatewaysById = HashBiMap.create();
private final Map<Integer, Byte> gatewaysByPower = new HashMap<>();
public RedstoneGateways(Plugin plugin) {
this.plugin = plugin;
}
/* Event */
int onPlace(Block block) {
var repeaterId = ThreadLocalRandom.current().nextInt() & 0x7FFFFFF0;
gatewaysById.put(repeaterId, block.getLocation());
block.setMetadata("rid", new FixedMetadataValue(plugin, repeaterId));
RedstoneStore.getInstance().saveRepeaterData(block.getLocation(), repeaterId, (byte) 0);
return repeaterId;
}
void onBreak(int repeaterId) {
delete(repeaterId);
}
void delete(int repeaterId) {
var location = gatewaysById.remove(repeaterId);
if (location == null) return;
gatewaysByPower.remove(repeaterId);
RedstoneStore.getInstance().deleteSavedRepeaterData(location);
}
/* Get functions */
private boolean isValid(int repeaterId) {
// check if there's a repeater with such ID stored
// if not, we're not loading because it's loaded as the block is
var loc = gatewaysById.get(repeaterId);
if (loc == null) {
DebugLogger.fine("isValid: Delete because no loc");
delete(repeaterId);
return false;
}
// check if chunk the block is in is loaded
// you may think it could be simplified, but it can't without loading the chunk (to check if it's loaded)
var isLoaded = loc.getWorld().isChunkLoaded(loc.getBlockX() / 16, loc.getBlockZ() / 16);
if (!isLoaded) return false;
// check if the block is correct type
if (loc.getBlock().getType() != Material.DAYLIGHT_DETECTOR) {
DebugLogger.fine("isValid: Delete because not sensor");
delete(repeaterId);
return false;
}
// check if the block has the same ID bound
var meta = loc.getBlock().getMetadata("rid");
if (meta.isEmpty() || meta.getFirst().asInt() != repeaterId) {
DebugLogger.fine("isValid: Delete because no meta");
delete(repeaterId);
return false;
}
return true;
}
public int getId(Block block) {
if (block.hasMetadata("rid")) {
var id = block.getMetadata("rid").getFirst().asInt();
return id;
}
var id = gatewaysById.inverse().get(block.getLocation());
if (id == null) {
// not in memory, check if repeater
var d = RedstoneStore.getInstance().getSavedRepeaterData(block.getLocation());
if (d == null) {
block.setMetadata("rid", new FixedMetadataValue(plugin, Integer.MIN_VALUE));
return Integer.MIN_VALUE;
}
id = d.getKey();
block.setMetadata("rid", new FixedMetadataValue(plugin, id));
gatewaysById.put(id, block.getLocation());
gatewaysByPower.put(id, d.getValue());
}
if (!isValid(id)) return Integer.MIN_VALUE;
return id;
}
Block getBlock(int repeaterId) {
var location = gatewaysById.get(repeaterId);
if (location == null) return null;
if (!isValid(repeaterId)) return null;
var storedId = location.getBlock().getMetadata("rid").getFirst().asInt();
if (storedId != repeaterId) {
DebugLogger.fine("attempted retrieve, but doesn't exist, deleting " + repeaterId);
delete(repeaterId);
return null;
}
DebugLogger.fine("retrieved " + repeaterId);
return location.getBlock();
}
/* Control functions */
byte getInboundPower(int repeaterId) {
var block = getBlock(repeaterId);
if (block == null) return -1;
block.getWorld().spawnParticle(Particle.LAVA, block.getLocation().add(0.5, 0.5, 0.5), 3);
var power = (byte) block.getBlockPower();
DebugLogger.fine("Got " + repeaterId + " receives " + power);
return power;
}
byte getOutboundPower(int repeaterId) {
var block = getBlock(repeaterId);
if (block == null) return -1;
var power = gatewaysByPower.getOrDefault(repeaterId, (byte) 0);
DebugLogger.fine("Got " + repeaterId + " outputs " + power);
return power;
}
void setPower(int repeaterId, byte power) {
if (power < 0 || power > 15)
throw new IllegalArgumentException("Power should be 0-15, but is " + power);
var block = getBlock(repeaterId);
if (block == null) return;
var data = (AnaloguePowerable) block.getBlockData();
gatewaysByPower.put(repeaterId, power);
data.setPower(power);
block.getWorld().spawnParticle(Particle.LAVA, block.getLocation().add(0.5, 0.5, 0.5), 3);
DebugLogger.fine("Set power of " + repeaterId + " to " + power);
}
}

View file

@ -1,91 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.Language;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.HoverEvent;
import net.md_5.bungee.api.chat.hover.content.Text;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.bukkit.event.player.PlayerInteractEvent;
public class RedstoneListener implements Listener {
private final RedstoneGateways redstoneGateways;
private final GatewayItem gatewayItem;
public RedstoneListener(RedstoneGateways redstoneGateways, GatewayItem gatewayItem) {
this.redstoneGateways = redstoneGateways;
this.gatewayItem = gatewayItem;
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
if (!gatewayItem.isGateway(event.getItemInHand())) return;
var block = event.getBlockPlaced();
var id = redstoneGateways.onPlace(block);
DebugLogger.fine("Gateway placed: " + id);
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
var id = redstoneGateways.getId(event.getBlock());
if (id == Integer.MIN_VALUE) return;
redstoneGateways.onBreak(id);
if (event.getPlayer().getInventory().getItemInMainHand().containsEnchantment(Enchantment.SILK_TOUCH)) {
event.setDropItems(false);
event.getBlock().getWorld().dropItemNaturally(event.getBlock().getLocation(), gatewayItem.itemStack());
}
DebugLogger.fine("Gateway broken: " + id);
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if (!event.getPlayer().isSneaking()) return;
var id = redstoneGateways.getId(event.getClickedBlock());
if (id == Integer.MIN_VALUE) return;
// TODO find a less lame way of showing ID
var component = new ComponentBuilder("Gateway ID: ").color(ChatColor.GOLD)
.append(String.valueOf(id)).color(ChatColor.AQUA)
.event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text(Language.getString("clickToCopy"))))
.event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, String.valueOf(id)))
.build();
event.getPlayer().spigot().sendMessage(component);
// cancel block place
event.setCancelled(true);
}
@EventHandler
public void onBlockRedstone(BlockRedstoneEvent event) {
var block = event.getBlock();
var id = redstoneGateways.getId(block);
if (id == Integer.MIN_VALUE) return;
event.setNewCurrent(redstoneGateways.getOutboundPower(id));
DebugLogger.fine("Gateway redstone event: " + id);
}
}

View file

@ -1,99 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksConfig;
import eu.m724.tweaks.TweaksModule;
import java.io.IOException;
import java.net.*;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
public class RedstoneModule extends TweaksModule {
private final RedstoneGateways redstoneGateways = new RedstoneGateways(getPlugin());
private DatagramSocket socket;
private RedstoneStateUpdateRunnable runnable;
@Override
protected void onInit() {
RedstoneStore.init(getPlugin());
var gatewayItem = new GatewayItem(getPlugin());
registerEvents(new RedstoneListener(redstoneGateways, gatewayItem));
registerCommand("retstone", new RedstoneCommands(gatewayItem));
this.runnable = new RedstoneStateUpdateRunnable(redstoneGateways);
this.runnable.runTaskTimer(getPlugin(), 0, 20); // TODO configurable
var listenAddress = TweaksConfig.getConfig().redstoneListen().split(":");
InetSocketAddress bindAddress;
if (listenAddress.length == 1) {
bindAddress = new InetSocketAddress(Integer.parseInt(listenAddress[0]));
} else {
bindAddress = new InetSocketAddress(listenAddress[0], Integer.parseInt(listenAddress[1]));
}
try {
initSocket(bindAddress);
} catch (SocketException e) {
throw new RuntimeException("Starting socket", e);
}
}
private void initSocket(SocketAddress bindAddress) throws SocketException {
socket = new DatagramSocket(bindAddress);
Executors.newSingleThreadExecutor().execute(() -> {
byte[] buf = new byte[4];
while (!socket.isClosed()) {
DatagramPacket packet
= new DatagramPacket(buf, buf.length);
try {
socket.receive(packet);
} catch (IOException e) {
DebugLogger.severe("Error reading packet: " + e.getMessage());
continue;
}
boolean write = (buf[0] >> 7 & 1) == 1;
byte data = (byte) (buf[3] & 0xF);
int gatewayId = ((buf[0] & 0x7F) << 24) | ((buf[1] & 0xFF) << 16) | ((buf[2] & 0xFF) << 8) | (buf[3] & 0xF0);
if (write) {
enqueueUpdate(gatewayId, data);
} else {
var newPacket = new DatagramPacket(new byte[1], 1, packet.getSocketAddress());
enqueueRetrieve(gatewayId, value -> {
DebugLogger.fine("Retrieved for " + gatewayId + " power " + value);
newPacket.setData(new byte[] { (byte) Math.max(value, 0) });
try {
socket.send(newPacket);
} catch (IOException e) {
throw new RuntimeException("Sending response to get repeater value", e);
}
});
}
}
});
}
private void enqueueUpdate(int gatewayId, byte power) {
DebugLogger.finer("Update enqueued " + gatewayId + " " + power);
runnable.enqueueUpdate(gatewayId, power);
}
private void enqueueRetrieve(int gatewayId, Consumer<Byte> consumer) {
DebugLogger.finer("Retrieve enqueued " + gatewayId);
runnable.enqueueRetrieve(gatewayId, consumer);
}
}

View file

@ -1,44 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
public class RedstoneStateUpdateRunnable extends BukkitRunnable {
private Map<Integer, Byte> updateQueue = new HashMap<>();
private Map<Integer, Consumer<Byte>> retrieveQueue = new HashMap<>();
private final RedstoneGateways redstoneGateways;
RedstoneStateUpdateRunnable(RedstoneGateways redstoneGateways) {
this.redstoneGateways = redstoneGateways;
}
void enqueueUpdate(int gatewayId, byte power) {
updateQueue.put(gatewayId, power);
}
void enqueueRetrieve(int repeaterId, Consumer<Byte> consumer) {
retrieveQueue.put(repeaterId, consumer);
}
@Override
public void run() {
var updateQueue = this.updateQueue;
this.updateQueue = new HashMap<>();
var retrieveQueue = this.retrieveQueue;
this.retrieveQueue = new HashMap<>();
updateQueue.forEach((key, value) -> redstoneGateways.setPower(key, value));
retrieveQueue.forEach((key, value) -> value.accept(redstoneGateways.getInboundPower(key)));
}
}

View file

@ -1,78 +0,0 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.redstone;
import com.google.common.primitives.Ints;
import eu.m724.tweaks.DebugLogger;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Location;
import org.bukkit.plugin.Plugin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class RedstoneStore {
private static RedstoneStore INSTANCE;
private final File directory;
private RedstoneStore(Plugin plugin) {
this.directory = new File(plugin.getDataFolder(), "storage/redstone");
directory.mkdirs();
}
static void init(Plugin plugin) {
INSTANCE = new RedstoneStore(plugin);
}
static RedstoneStore getInstance() {
return INSTANCE;
}
Pair<Integer, Byte> getSavedRepeaterData(Location location) {
var file = getFile(location);
if (!file.exists()) return null;
byte[] bytes;
try {
// TODO read just 4 bytes
bytes = Files.readAllBytes(file.toPath());
} catch (IOException e) {
throw new RuntimeException("Loading saved gateway data", e);
}
var gatewayId = Ints.fromByteArray(bytes) & ~0xF;
var powerLevel = (byte) (bytes[3] & 0xF);
DebugLogger.fine("load " + location + " " + gatewayId + " " + powerLevel);
return Pair.of(gatewayId, powerLevel);
}
void saveRepeaterData(Location location, int gatewayId, byte powerLevel) {
var file = getFile(location);
byte[] bytes = Ints.toByteArray((gatewayId & ~0xF) | (powerLevel & 0xF));
try {
Files.write(file.toPath(), bytes);
} catch (IOException e) {
throw new RuntimeException("Saving repeater data", e);
}
DebugLogger.fine("save " + location + " " + gatewayId + " " + powerLevel);
}
void deleteSavedRepeaterData(Location location) {
getFile(location).delete();
}
private File getFile(Location location) {
return new File(directory, location.getWorld().getName() + " " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ());
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
@ -21,7 +21,6 @@ import java.util.Set;
public class SleepListener implements Listener { public class SleepListener implements Listener {
private final boolean instant = TweaksConfig.getConfig().sleepInstant(); private final boolean instant = TweaksConfig.getConfig().sleepInstant();
private final double heal = TweaksConfig.getConfig().sleepHeal() * 2; // hearts to half hearts
private final Set<Player> skippedCurrentNight = new HashSet<>(); private final Set<Player> skippedCurrentNight = new HashSet<>();
private long lastDay = 0; private long lastDay = 0;
@ -31,6 +30,7 @@ public class SleepListener implements Listener {
if (event.getBedEnterResult() == PlayerBedEnterEvent.BedEnterResult.OK) { if (event.getBedEnterResult() == PlayerBedEnterEvent.BedEnterResult.OK) {
SleepState.playersSleeping++; SleepState.playersSleeping++;
if (instant) {
World world = event.getPlayer().getWorld(); World world = event.getPlayer().getWorld();
long day = world.getFullTime() / 24000; long day = world.getFullTime() / 24000;
@ -38,13 +38,10 @@ public class SleepListener implements Listener {
lastDay = day; lastDay = day;
if (!skippedCurrentNight.contains(event.getPlayer())) { if (!skippedCurrentNight.contains(event.getPlayer())) {
if (instant) {
double onePlayerRatio = 1 / (event.getPlayer().getServer().getOnlinePlayers().size() * (world.getGameRuleValue(GameRule.PLAYERS_SLEEPING_PERCENTAGE) / 100.0)); double onePlayerRatio = 1 / (event.getPlayer().getServer().getOnlinePlayers().size() * (world.getGameRuleValue(GameRule.PLAYERS_SLEEPING_PERCENTAGE) / 100.0));
world.setTime(Math.min(world.getTime() + (long) (10917 * onePlayerRatio), 23459)); world.setTime(Math.min(world.getTime() + (long) (10917 * onePlayerRatio), 23459));
skippedCurrentNight.add(event.getPlayer()); skippedCurrentNight.add(event.getPlayer());
} }
event.getPlayer().setHealth(event.getPlayer().getHealth() + heal);
} }
} }
@ -56,9 +53,8 @@ public class SleepListener implements Listener {
} }
@EventHandler @EventHandler
public void onTimeSkip(TimeSkipEvent event) { public void onPlayerBedLeave(TimeSkipEvent event) {
if (event.getSkipReason() == TimeSkipEvent.SkipReason.NIGHT_SKIP) { if (event.getSkipReason() == TimeSkipEvent.SkipReason.NIGHT_SKIP)
event.setCancelled(true); event.setCancelled(true);
} }
}
} }

View file

@ -0,0 +1,18 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.sleep;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.plugin.Plugin;
public class SleepManager {
public void init(Plugin plugin) {
plugin.getServer().getPluginManager().registerEvents(new SleepListener(), plugin);
if (!TweaksConfig.getConfig().sleepInstant())
new TimeForwardRunnable(plugin).runTaskTimer(plugin, 0, 1); // TODO maybe not
}
}

View file

@ -1,19 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.sleep;
import eu.m724.tweaks.TweaksModule;
public class SleepModule extends TweaksModule {
@Override
protected void onInit() {
registerEvents(new SleepListener());
if (!getConfig().sleepInstant())
new TimeForwardRunnable(getPlugin()).runTaskTimer(getPlugin(), 0, 1); // TODO maybe not
}
}

View file

@ -1,68 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.swing;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class SwingModule extends TweaksModule implements Listener {
private final Set<Material> tools = new HashSet<>();
@Override
protected void onInit() {
Arrays.stream(Material.values())
.filter(m -> m.name().contains("SWORD"))
.forEach(tools::add);
DebugLogger.finer("Tools: " + tools.size());
registerEvents(this);
}
@EventHandler
public void onBreak(BlockBreakEvent event) {
var type = event.getBlock().getType();
if (type.isOccluding()) return;
var player = event.getPlayer();
var tool = player.getInventory().getItemInMainHand().getType();
Entity entity = null;
if (tools.contains(tool)) { // if sword, raycast to hit farther
var result = player.getWorld().rayTraceEntities(
player.getEyeLocation(),
player.getEyeLocation().getDirection(),
player.getAttribute(Attribute.PLAYER_ENTITY_INTERACTION_RANGE).getValue(),
e -> e != player
);
if (result != null)
entity = result.getHitEntity();
} else {
entity = event.getBlock().getWorld()
.getNearbyEntities(event.getBlock().getLocation().add(0.5, 0.5, 0.5), 0.5, 0.5, 0.5)
.stream().filter(e -> (e instanceof LivingEntity && e != player))
.findFirst().orElse(null);
}
if (entity != null) {
player.attack(entity);
DebugLogger.fine("Swing " + player.getName() + " hit " + entity.getName());
}
}
}

View file

@ -36,6 +36,7 @@ public class PluginScanner {
Set<SpigotResource> spigotResources = new HashSet<>(); Set<SpigotResource> spigotResources = new HashSet<>();
for (Plugin plugin : plugins) { for (Plugin plugin : plugins) {
// System.out.println("Found " + plugin.getName());
String pluginName = plugin.getName(); String pluginName = plugin.getName();
if (!configuration.isSet(pluginName)) { if (!configuration.isSet(pluginName)) {

View file

@ -1,17 +1,16 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.updater; package eu.m724.tweaks.updater;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.Language; import eu.m724.tweaks.Language;
import eu.m724.tweaks.updater.cache.VersionedResource; import eu.m724.tweaks.updater.cache.VersionedResource;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.time.LocalTime; import java.time.LocalTime;
@ -24,26 +23,25 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class UpdateChecker extends BukkitRunnable { public class UpdateChecker extends BukkitRunnable {
private final Logger logger;
private final File cacheFile;
private final Set<VersionedResource> resources; private final Set<VersionedResource> resources;
private final Logger logger;
static final Set<VersionedResource> availableUpdates = new HashSet<>(); static final Set<VersionedResource> availableUpdates = new HashSet<>();
static LocalTime lastChecked = null; static LocalTime lastChecked = null;
UpdateChecker(Logger logger, File cacheFile, Set<VersionedResource> resources) { UpdateChecker(Plugin plugin, Set<VersionedResource> resources) {
this.logger = logger; this.logger = Logger.getLogger(plugin.getLogger().getName() + "." + getClass().getSimpleName());
this.cacheFile = cacheFile;
this.resources = resources; // TODO make a copy? this.resources = resources; // TODO make a copy?
} }
private void checkAll() { private void checkAll() {
DebugLogger.fine("Checking for updates"); //logger.info("Checking for updates");
lastChecked = LocalTime.now(ZoneOffset.UTC); lastChecked = LocalTime.now(ZoneOffset.UTC);
availableUpdates.clear(); availableUpdates.clear();
for (VersionedResource versionedResource : Set.copyOf(resources)) { for (VersionedResource versionedResource : Set.copyOf(resources)) {
String pluginName = versionedResource.resource().plugin().getName(); String pluginName = versionedResource.resource().plugin().getName();
//logger.info(versionedResource.resource().resourceId() + " " + versionedResource.resource().plugin().getName());
int page = versionedResource.running() != null ? versionedResource.running().page() : 1; int page = versionedResource.running() != null ? versionedResource.running().page() : 1;
try { try {
@ -51,19 +49,20 @@ public class UpdateChecker extends BukkitRunnable {
if (!versionedResource.equals(newResource)) { if (!versionedResource.equals(newResource)) {
resources.remove(versionedResource); resources.remove(versionedResource);
if (newResource.running() == null) { if (newResource.running() == null) {
logger.warning("Unable to find installed version of %s".formatted(pluginName)); logger.info("Unable to find installed version of %s".formatted(pluginName));
if (versionedResource.running() != null) { if (versionedResource.running() != null) {
logger.warning("Did you downgrade %s? If so, clear cache".formatted(pluginName)); logger.info("Did you downgrade %s? If so, clear cache".formatted(pluginName));
} }
} else { } else {
if (!newResource.running().equals(newResource.latest())) { if (!newResource.running().equals(newResource.latest())) {
availableUpdates.add(newResource); availableUpdates.add(newResource);
//logger.info("Update available for %s. %d -> %d".formatted(pluginName, newResource.running().updateId(), newResource.latest().updateId()));
} }
} }
resources.add(newResource); resources.add(newResource);
} }
} catch (CompletionException e) { } catch (CompletionException e) {
DebugLogger.severe("Unable to refresh %s: %s".formatted(pluginName, e.getMessage())); logger.severe("Unable to refresh %s: %s".formatted(pluginName, e.getMessage()));
} }
} }
} }
@ -71,7 +70,7 @@ public class UpdateChecker extends BukkitRunnable {
private void alert() { private void alert() {
int n = availableUpdates.size(); int n = availableUpdates.size();
if (n == 0) return; if (n == 0) return;
logger.info(Language.getString("updateAvailableNotice", n)); logger.info(Language.getString("updateAvailableNotice").formatted(n));
availableUpdates.stream() availableUpdates.stream()
.map(u -> "- %s (%s -> %s)".formatted(u.resource().name(), u.running().label(), u.latest().label())) .map(u -> "- %s (%s -> %s)".formatted(u.resource().name(), u.running().label(), u.latest().label()))
@ -82,9 +81,7 @@ public class UpdateChecker extends BukkitRunnable {
public void run() { public void run() {
checkAll(); checkAll();
DebugLogger.finer("Done checking, now saving"); try (FileOutputStream outputStream = new FileOutputStream(UpdaterManager.cacheFile)) {
cacheFile.getParentFile().mkdirs();
try (FileOutputStream outputStream = new FileOutputStream(cacheFile)) {
VersionCheckCache.writeAll(outputStream, resources.stream().map(VersionedResource::running).filter(Objects::nonNull).collect(Collectors.toSet())); VersionCheckCache.writeAll(outputStream, resources.stream().map(VersionedResource::running).filter(Objects::nonNull).collect(Collectors.toSet()));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View file

@ -35,7 +35,7 @@ public class UpdaterCommands implements CommandExecutor {
if (n > 0) { if (n > 0) {
sender.spigot().sendMessage( sender.spigot().sendMessage(
Language.getComponent("updateAvailableNotice", ChatColor.GRAY, n) new ComponentBuilder(Language.getString("updateAvailableNotice").formatted(n)).color(ChatColor.GRAY).build()
); );
int i = 0; int i = 0;
@ -45,7 +45,7 @@ public class UpdaterCommands implements CommandExecutor {
); );
} }
} else { } else {
sender.spigot().sendMessage(Language.getComponent("updatesNoUpdates", ChatColor.GREEN, lastChecked)); sender.sendMessage(Language.getString("updatesNoUpdates").formatted(lastChecked));
} }
return true; return true;
@ -71,7 +71,7 @@ public class UpdaterCommands implements CommandExecutor {
.event( .event(
new HoverEvent( new HoverEvent(
HoverEvent.Action.SHOW_TEXT, HoverEvent.Action.SHOW_TEXT,
new Text(Language.getString("updatesClickToOpen", v.latest().description().title())) new Text(Language.getString("updatesClickToOpen").formatted(v.latest().description().title()))
) )
) )
.build(); .build();

View file

@ -1,16 +1,15 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.updater; package eu.m724.tweaks.updater;
import eu.m724.tweaks.DebugLogger;
import eu.m724.tweaks.TweaksModule;
import eu.m724.tweaks.updater.cache.ResourceVersion; import eu.m724.tweaks.updater.cache.ResourceVersion;
import eu.m724.tweaks.updater.cache.SpigotResource; import eu.m724.tweaks.updater.cache.SpigotResource;
import eu.m724.tweaks.updater.cache.VersionedResource; import eu.m724.tweaks.updater.cache.VersionedResource;
import org.bukkit.plugin.Plugin;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -20,29 +19,28 @@ import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class UpdaterModule extends TweaksModule { public class UpdaterManager {
@Override static File cacheFile;
protected void onInit() {
// scan installed plugins private final Plugin plugin;
Set<SpigotResource> resources;
try { public UpdaterManager(Plugin plugin) {
resources = new PluginScanner(getPlugin()).load(); this.plugin = plugin;
} catch (IOException e) { cacheFile = new File(plugin.getDataFolder(), "cache/updater");
throw new RuntimeException("Loading plugins", e);
} }
public void init() throws IOException {
// scan installed plugins
Set<SpigotResource> resources = new PluginScanner(plugin).load();
cacheFile.getParentFile().mkdirs(); // TODO move this somewhere else
// load installed versions from cache // load installed versions from cache
var cacheFile = new File(getPlugin().getDataFolder(), "storage/cache/updater");
Set<ResourceVersion> installedVersions; Set<ResourceVersion> installedVersions;
try (FileInputStream inputStream = new FileInputStream(cacheFile)) { try (FileInputStream inputStream = new FileInputStream(cacheFile)) {
installedVersions = VersionCheckCache.loadAll(inputStream); installedVersions = VersionCheckCache.loadAll(inputStream);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
installedVersions = new HashSet<>(); installedVersions = new HashSet<>();
} catch (IOException e) {
DebugLogger.warning("Error loading installed version cache, starting fresh. " + e.getMessage());
installedVersions = new HashSet<>();
} }
final Set<ResourceVersion> ivf = installedVersions; final Set<ResourceVersion> ivf = installedVersions;
@ -52,9 +50,7 @@ public class UpdaterModule extends TweaksModule {
)).collect(Collectors.toSet()); )).collect(Collectors.toSet());
new UpdateChecker(getPlugin().getLogger(), cacheFile, versionedResources) new UpdateChecker(plugin, versionedResources)
.runTaskTimerAsynchronously(getPlugin(), 600, 12 * 3600 * 20); // 12 hours .runTaskTimerAsynchronously(plugin, 600, 12 * 3600 * 20);
registerCommand("updates", new UpdaterCommands());
} }
} }

View file

@ -1,29 +1,18 @@
/* /*
* Copyright (C) 2025 Minecon724 * Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file * Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text. * in the project root for the full license text.
*/ */
package eu.m724.tweaks.worldborder; package eu.m724.tweaks.worldborder;
import eu.m724.tweaks.TweaksModule;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; import org.bukkit.craftbukkit.v1_21_R1.CraftWorld;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldLoadEvent;
public class WorldBorderExpandModule extends TweaksModule implements Listener { public class WorldBorderExpanderListener implements Listener {
@Override
protected void onInit() {
registerEvents(this);
// because the plugin loads "post world"
getPlugin().getServer().getWorlds().forEach(w -> {
onWorldLoad(new WorldLoadEvent(w));
});
}
@EventHandler @EventHandler
public void onWorldLoad(WorldLoadEvent event) { public void onWorldLoad(WorldLoadEvent event) {
ServerLevel level = ((CraftWorld) event.getWorld()).getHandle(); ServerLevel level = ((CraftWorld) event.getWorld()).getHandle();

View file

@ -1,51 +0,0 @@
/*
* Copyright (C) 2025 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.worldborder;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.PacketContainer;
import eu.m724.tweaks.TweaksModule;
import java.nio.ByteBuffer;
public class WorldBorderHideModule extends TweaksModule {
private static final int EXTENSION_RADIUS = 512;
@Override
protected void onInit() {
getPlugin().getServer().getMessenger().registerOutgoingPluginChannel(getPlugin(), "tweaks724:worldborder");
byte[] infoArray = ByteBuffer.allocate(4).putInt(EXTENSION_RADIUS).array();
onPacketSend(PacketType.Play.Server.INITIALIZE_BORDER, (event) -> {
PacketContainer packet = event.getPacket();
// old diameter
packet.getDoubles().write(2, packet.getDoubles().read(2) + EXTENSION_RADIUS * 2);
// new diameter
packet.getDoubles().write(3, packet.getDoubles().read(3) + EXTENSION_RADIUS * 2);
// border radius
packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
// warning distance
packet.getIntegers().write(1, packet.getIntegers().read(1) + EXTENSION_RADIUS);
event.getPlayer().sendPluginMessage(getPlugin(), "tweaks724:worldborder", infoArray);
});
onPacketSend(PacketType.Play.Server.SET_BORDER_SIZE, (event) -> {
PacketContainer packet = event.getPacket();
// diameter
packet.getDoubles().write(0, packet.getDoubles().read(0) + EXTENSION_RADIUS * 2);
});
onPacketSend(PacketType.Play.Server.SET_BORDER_WARNING_DISTANCE, (event) -> {
PacketContainer packet = event.getPacket();
// warning distance
packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
});
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.worldborder;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import org.bukkit.plugin.Plugin;
import java.nio.ByteBuffer;
public class WorldBorderHider {
private static final int EXTENSION_RADIUS = 512;
public void init(Plugin plugin) {
plugin.getServer().getMessenger().registerOutgoingPluginChannel(plugin, "tweaks724:worldborder");
byte[] infoArray = ByteBuffer.allocate(4).putInt(EXTENSION_RADIUS).array();
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.INITIALIZE_BORDER
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
// old diameter
packet.getDoubles().write(2, packet.getDoubles().read(2) + EXTENSION_RADIUS * 2);
// new diameter
packet.getDoubles().write(3, packet.getDoubles().read(3) + EXTENSION_RADIUS * 2);
// border radius
packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
// warning distance
packet.getIntegers().write(1, packet.getIntegers().read(1) + EXTENSION_RADIUS);
event.getPlayer().sendPluginMessage(plugin, "tweaks724:worldborder", infoArray);
}
});
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.SET_BORDER_SIZE
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
// diameter
packet.getDoubles().write(0, packet.getDoubles().read(0) + EXTENSION_RADIUS * 2);
}
});
ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(
plugin,
ListenerPriority.NORMAL,
PacketType.Play.Server.SET_BORDER_WARNING_DISTANCE
) {
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
// warning distance
packet.getIntegers().write(0, packet.getIntegers().read(0) + EXTENSION_RADIUS);
}
});
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2024 Minecon724
* Tweaks724 is licensed under the GNU General Public License. See the LICENSE.md file
* in the project root for the full license text.
*/
package eu.m724.tweaks.worldborder;
import eu.m724.tweaks.TweaksConfig;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.plugin.Plugin;
public class WorldBorderManager {
public void init(Plugin plugin) {
if (TweaksConfig.getConfig().worldborderExpand()) {
WorldBorderExpanderListener wbrl = new WorldBorderExpanderListener();
plugin.getServer().getPluginManager().registerEvents(wbrl, plugin);
// because the plugin loads "post world"
plugin.getServer().getWorlds().forEach(w -> {
wbrl.onWorldLoad(new WorldLoadEvent(w));
});
}
if (TweaksConfig.getConfig().worldborderHide()) {
new WorldBorderHider().init(plugin);
}
}
}

View file

@ -83,9 +83,6 @@ hardcore:
# Makes sleeping # Makes sleeping
# And adds a nice animation # And adds a nice animation
# Percentage: playersSleepingPercentage gamerule
# If instant: how much % of players to skip the night
# If not: how much % make skipping full speed
sleep: sleep:
enabled: true enabled: true
# This gives every player a "share" of the night # This gives every player a "share" of the night
@ -93,8 +90,9 @@ sleep:
# For example, if 5 players online and night is 5 minutes, one can go to sleep and skip 1 minute of the night # For example, if 5 players online and night is 5 minutes, one can go to sleep and skip 1 minute of the night
# Leaving the bed and reentering it does nothing # Leaving the bed and reentering it does nothing
instant: false instant: false
# How many hearts to heal after sleeping # Percentage: playersSleepingPercentage gamerule
heal: 2.0 # If instant: how much % of players to skip the night
# If not: how much % make skipping full speed
# "Hostname" authentication # "Hostname" authentication
# This makes a player need to join a unique hostname like "asd123.example.com" where "asd123" is the key # This makes a player need to join a unique hostname like "asd123.example.com" where "asd123" is the key
@ -105,37 +103,7 @@ auth:
# The domain of the server. Doesn't do anything other than showing in /tauth new # The domain of the server. Doesn't do anything other than showing in /tauth new
domain: "replace.me" domain: "replace.me"
# Adds gateways emitting redstone, controlled over internet
# https://git.m724.eu/Minecon724/tweaks724/src/branch/master/RETSTONE.md
retstone:
enabled: true
# This takes host:port, listens on UDP
listen: 127.0.0.1:57931
# Knockback dealt BY those entities is multiplied by value
# Entity must be one of https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html
# 1 means no change
knockback:
player: 0.7
tnt: 5
creeper: 0.7
# Kills server after /servkill or HTTP request
# https://git.m724.eu/Minecon724/tweaks724/src/branch/master/KILLSWITCH.md
killswitch:
enabled: true
# This takes host:port, starts an HTTP server
# To disable HTTP server, set to null
listen: 127.0.0.1:57932
# Swing through grass (and alike)
# If using sword, you can also hit behind the grass
# If not, you can only hit the entity in the grass
swing:
enabled: true
# Finally, thank you for downloading Tweaks724, I hope you enjoy! # Finally, thank you for downloading Tweaks724, I hope you enjoy!
# Don't modify unless told to # Don't modify unless told to
magic number don't modify this: 2 magic number don't modify this: 1

View file

@ -7,5 +7,3 @@ Random MOTD for every ping
Or just one line. If it doesn't fit it will be passed onto the second line. Or just one line. If it doesn't fit it will be passed onto the second line.
Put your own files in this directory Put your own files in this directory
["",{"text":"Also supports","color":"#A18D94"},{"text":" JSON","color":"#FF00FF"},{"text":"\n"},{"text":"Java","color":"dark_aqua"},{"text":"Script ","color":"aqua"},{"text":"Object","color":"gray"},{"text":" Notation","color":"red"}]

View file

@ -34,12 +34,6 @@ commands:
emergencyalert: emergencyalert:
description: Send emergency alert description: Send emergency alert
permission: tweaks724.emergencyalert permission: tweaks724.emergencyalert
retstone:
description: Retstone commands
permission: tweaks724.retstone
servkill:
description: Immediately stop the server
permission: tweaks724.servkill
permissions: permissions:
tweaks724: tweaks724:
@ -55,7 +49,4 @@ permissions:
default: op default: op
emergencyalert: emergencyalert:
default: op default: op
retstone:
default: op
servkill:
default: false

View file

@ -4,13 +4,8 @@
# in the project root for the full license text. # in the project root for the full license text.
# #
languageNotice = Language: %s (%s)
# Language name in your language
language = English
# Language name in English
languageEnglish = English
updateAvailableNotice = Available updates (%d): updateAvailableNotice = Available updates (%d):
pomodoroEndKick = Break time! Come back in 5 minutes.
# Used in /updates # Used in /updates
updatesNotChecked = Not checked yet updatesNotChecked = Not checked yet
@ -29,7 +24,3 @@ chatAlreadyHere = You're already in this room
authKickWrongKey = You're connecting to the wrong server address. You must connect to the one you're registered to. authKickWrongKey = You're connecting to the wrong server address. You must connect to the one you're registered to.
# If force is enabled and player is not registered. Changing this reveals you're using this plugin # If force is enabled and player is not registered. Changing this reveals you're using this plugin
authKickUnregistered = You are not whitelisted on this server! authKickUnregistered = You are not whitelisted on this server!
redstoneGatewayItem = Redstone gateway
clickToCopy = Click to copy to clipboard