Merge branch 'master' into refactor/1.15
# Conflicts: # arclight-common/src/main/java/io/izzel/arclight/common/bridge/server/management/PlayerListBridge.java # arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/item/ItemStackMixin.java # arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/server/management/PlayerListMixin.java # arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightConnector.java # arclight-common/src/main/resources/META-INF/accesstransformer.cfg # arclight-common/src/main/resources/mixins.arclight.core.json # arclight-forge-1.14/build.gradle # arclight-forge-1.14/src/main/java/io/izzel/arclight/server/Main.java # settings.gradle
This commit is contained in:
commit
c538046c1a
15
README.md
15
README.md
@ -9,7 +9,7 @@ A Bukkit server implementation utilizing Mixin.
|
||||
## Installing
|
||||
|
||||
1. Download the jar from [release page](https://github.com/IzzelAliz/Arclight/releases) or [build server](https://ci.appveyor.com/project/IzzelAliz/arclight/build/artifacts).
|
||||
2. Launch with command `java -jar arclight-coremod-xxx.jar`
|
||||
2. Launch with command `java -jar arclight-forge-mcversion-xxx.jar nogui`. The `nogui` argument will disable the server control panel.
|
||||
|
||||
## Support
|
||||
|
||||
@ -19,7 +19,18 @@ QQ Group chat 3556966
|
||||
|
||||
## Contributing
|
||||
|
||||
To setup development workspace, clone this repository and import the project.
|
||||
This project uses Gradle 4.9 as build tool with [arclight-gradle-plugin](https://github.com/IzzelAliz/arclight-gradle-plugin).
|
||||
|
||||
To setup development workspace, clone this repository first, and type
|
||||
```
|
||||
./gradlew remapSpigotJar idea
|
||||
```
|
||||
|
||||
This will generate proper spigot sources and srg mappings.
|
||||
|
||||
Finally, import the project. IntelliJ IDEA is the recommended IDE.
|
||||
|
||||
Due to a [MixinGradle bug](https://github.com/SpongePowered/MixinGradle/issues/9), you may build the project twice or the mixin shadows won't get reobfuscated.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@ -60,6 +60,7 @@ dependencies {
|
||||
compile 'mysql:mysql-connector-java:5.1.47'
|
||||
compile 'org.yaml:snakeyaml:1.23'
|
||||
compile project(':arclight-api')
|
||||
compile project(':i18n-config')
|
||||
}
|
||||
|
||||
remapSpigotJar {
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package io.izzel.arclight.common.bridge.network;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public interface NetworkManagerBridge {
|
||||
|
||||
UUID bridge$getSpoofedUUID();
|
||||
|
||||
void bridge$setSpoofedUUID(UUID spoofedUUID);
|
||||
|
||||
Property[] bridge$getSpoofedProfile();
|
||||
|
||||
void bridge$setSpoofedProfile(Property[] spoofedProfile);
|
||||
|
||||
SocketAddress bridge$getRawAddress();
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package io.izzel.arclight.common.bridge.network.login;
|
||||
|
||||
public interface ServerLoginNetHandlerBridge {
|
||||
|
||||
String bridge$getHostname();
|
||||
|
||||
void bridge$setHostname(String hostname);
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package io.izzel.arclight.common.bridge.server.management;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.play.server.SRespawnPacket;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@ -8,8 +9,10 @@ import net.minecraft.world.GameType;
|
||||
import net.minecraft.world.WorldType;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraft.network.login.ServerLoginNetHandler;
|
||||
import org.bukkit.craftbukkit.v.CraftServer;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public interface PlayerListBridge {
|
||||
@ -20,6 +23,8 @@ public interface PlayerListBridge {
|
||||
|
||||
CraftServer bridge$getCraftServer();
|
||||
|
||||
ServerPlayerEntity bridge$canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile, ServerLoginNetHandler handler);
|
||||
|
||||
boolean bridge$worldNoCollision(ServerWorld world, Entity entity);
|
||||
|
||||
void bridge$setSpawnPoint(ServerPlayerEntity player, BlockPos pos, boolean flag, DimensionType type, boolean flag1);
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
package io.izzel.arclight.common.mixin.bukkit;
|
||||
|
||||
import io.izzel.arclight.common.mod.util.log.ArclightPluginLogger;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginLogger;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import io.izzel.arclight.common.mod.util.ArclightPluginLogger;
|
||||
|
||||
@Mixin(JavaPlugin.class)
|
||||
public class JavaPluginMixin {
|
||||
|
||||
@ -591,9 +591,11 @@ public abstract class LivingEntityMixin extends EntityMixin implements LivingEnt
|
||||
|
||||
@Redirect(method = "heal", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;setHealth(F)V"))
|
||||
public void arclight$healEvent(LivingEntity livingEntity, float health) {
|
||||
EntityRegainHealthEvent.RegainReason regainReason = arclight$regainReason == null ? EntityRegainHealthEvent.RegainReason.CUSTOM : arclight$regainReason;
|
||||
arclight$regainReason = null;
|
||||
float f = this.getHealth();
|
||||
float amount = health - f;
|
||||
EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), amount, arclight$regainReason == null ? EntityRegainHealthEvent.RegainReason.CUSTOM : arclight$regainReason);
|
||||
EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), amount, regainReason);
|
||||
if (this.valid) {
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
package io.izzel.arclight.common.mixin.core.network;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import io.izzel.arclight.common.bridge.network.NetworkManagerBridge;
|
||||
import io.netty.channel.Channel;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mixin(NetworkManager.class)
|
||||
public class NetworkManagerMixin implements NetworkManagerBridge {
|
||||
|
||||
@Shadow public Channel channel;
|
||||
public java.util.UUID spoofedUUID;
|
||||
public com.mojang.authlib.properties.Property[] spoofedProfile;
|
||||
|
||||
@Override
|
||||
public UUID bridge$getSpoofedUUID() {
|
||||
return spoofedUUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setSpoofedUUID(UUID spoofedUUID) {
|
||||
this.spoofedUUID = spoofedUUID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property[] bridge$getSpoofedProfile() {
|
||||
return spoofedProfile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setSpoofedProfile(Property[] spoofedProfile) {
|
||||
this.spoofedProfile = spoofedProfile;
|
||||
}
|
||||
|
||||
public SocketAddress getRawAddress() {
|
||||
return this.channel.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SocketAddress bridge$getRawAddress() {
|
||||
return getRawAddress();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
package io.izzel.arclight.common.mixin.core.network.handshake;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import io.izzel.arclight.common.bridge.network.NetworkManagerBridge;
|
||||
import io.izzel.arclight.common.bridge.network.login.ServerLoginNetHandlerBridge;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.ProtocolType;
|
||||
import net.minecraft.network.handshake.ServerHandshakeNetHandler;
|
||||
import net.minecraft.network.handshake.client.CHandshakePacket;
|
||||
import net.minecraft.network.login.ServerLoginNetHandler;
|
||||
import net.minecraft.network.login.server.SDisconnectLoginPacket;
|
||||
import net.minecraft.network.status.ServerStatusNetHandler;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.SharedConstants;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraftforge.fml.server.ServerLifecycleHooks;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
|
||||
@Mixin(ServerHandshakeNetHandler.class)
|
||||
public class ServerHandshakeNetHandlerMixin {
|
||||
|
||||
private static final Gson gson = new Gson();
|
||||
private static final HashMap<InetAddress, Long> throttleTracker = new HashMap<>();
|
||||
private static int throttleCounter = 0;
|
||||
|
||||
@Shadow @Final private NetworkManager networkManager;
|
||||
@Shadow @Final private MinecraftServer server;
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
public void processHandshake(CHandshakePacket packetIn) {
|
||||
if (!ServerLifecycleHooks.handleServerLogin(packetIn, this.networkManager)) return;
|
||||
switch (packetIn.getRequestedState()) {
|
||||
case LOGIN: {
|
||||
this.networkManager.setConnectionState(ProtocolType.LOGIN);
|
||||
|
||||
|
||||
try {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long connectionThrottle = Bukkit.getServer().getConnectionThrottle();
|
||||
InetAddress address = ((InetSocketAddress) this.networkManager.getRemoteAddress()).getAddress();
|
||||
synchronized (throttleTracker) {
|
||||
if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) {
|
||||
throttleTracker.put(address, currentTime);
|
||||
TranslationTextComponent component = new TranslationTextComponent("Connection throttled! Please wait before reconnecting.");
|
||||
this.networkManager.sendPacket(new SDisconnectLoginPacket(component));
|
||||
this.networkManager.closeChannel(component);
|
||||
return;
|
||||
}
|
||||
throttleTracker.put(address, currentTime);
|
||||
++throttleCounter;
|
||||
if (throttleCounter > 200) {
|
||||
throttleCounter = 0;
|
||||
throttleTracker.entrySet().removeIf(entry -> entry.getValue() > connectionThrottle);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LogManager.getLogger().debug("Failed to check connection throttle", t);
|
||||
}
|
||||
|
||||
|
||||
if (packetIn.getProtocolVersion() > SharedConstants.getVersion().getProtocolVersion()) {
|
||||
TranslationTextComponent component = new TranslationTextComponent(MessageFormat.format(SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getVersion().getName()));
|
||||
this.networkManager.sendPacket(new SDisconnectLoginPacket(component));
|
||||
this.networkManager.closeChannel(component);
|
||||
break;
|
||||
}
|
||||
if (packetIn.getProtocolVersion() < SharedConstants.getVersion().getProtocolVersion()) {
|
||||
TranslationTextComponent component = new TranslationTextComponent(MessageFormat.format(SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getVersion().getName()));
|
||||
this.networkManager.sendPacket(new SDisconnectLoginPacket(component));
|
||||
this.networkManager.closeChannel(component);
|
||||
break;
|
||||
}
|
||||
this.networkManager.setNetHandler(new ServerLoginNetHandler(this.server, this.networkManager));
|
||||
|
||||
|
||||
if (SpigotConfig.bungee) {
|
||||
String[] split = packetIn.ip.split("\00");
|
||||
if (split.length == 3 || split.length == 4) {
|
||||
packetIn.ip = split[0];
|
||||
this.networkManager.socketAddress = new InetSocketAddress(split[1], ((InetSocketAddress) this.networkManager.getRemoteAddress()).getPort());
|
||||
((NetworkManagerBridge) this.networkManager).bridge$setSpoofedUUID(UUIDTypeAdapter.fromString(split[2]));
|
||||
} else {
|
||||
TranslationTextComponent component = new TranslationTextComponent("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
|
||||
this.networkManager.sendPacket(new SDisconnectLoginPacket(component));
|
||||
this.networkManager.closeChannel(component);
|
||||
return;
|
||||
}
|
||||
if (split.length == 4) {
|
||||
((NetworkManagerBridge) this.networkManager).bridge$setSpoofedProfile(gson.fromJson(split[3], Property[].class));
|
||||
}
|
||||
}
|
||||
((ServerLoginNetHandlerBridge) this.networkManager.getNetHandler()).bridge$setHostname(packetIn.ip + ":" + packetIn.port);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case STATUS: {
|
||||
this.networkManager.setConnectionState(ProtocolType.STATUS);
|
||||
this.networkManager.setNetHandler(new ServerStatusNetHandler(this.server, this.networkManager));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new UnsupportedOperationException("Invalid intention " + packetIn.getRequestedState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package io.izzel.arclight.common.mixin.core.network.handshake.client;
|
||||
|
||||
import net.minecraft.network.PacketBuffer;
|
||||
import net.minecraft.network.handshake.client.CHandshakePacket;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(CHandshakePacket.class)
|
||||
public class CHandshakePacketMixin {
|
||||
|
||||
@Redirect(method = "readPacketData", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketBuffer;readString(I)Ljava/lang/String;"))
|
||||
private String arclight$bungeeHostname(PacketBuffer packetBuffer, int maxLength) {
|
||||
return packetBuffer.readString(Short.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,21 @@
|
||||
package io.izzel.arclight.common.mixin.core.network.login;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import io.izzel.arclight.common.bridge.network.NetworkManagerBridge;
|
||||
import io.izzel.arclight.common.bridge.network.login.ServerLoginNetHandlerBridge;
|
||||
import io.izzel.arclight.common.bridge.server.MinecraftServerBridge;
|
||||
import io.izzel.arclight.common.bridge.server.management.PlayerListBridge;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.login.ServerLoginNetHandler;
|
||||
import net.minecraft.network.login.client.CEncryptionResponsePacket;
|
||||
import net.minecraft.network.login.client.CLoginStartPacket;
|
||||
import net.minecraft.network.login.server.SDisconnectLoginPacket;
|
||||
import net.minecraft.network.login.server.SEnableCompressionPacket;
|
||||
import net.minecraft.network.login.server.SEncryptionRequestPacket;
|
||||
import net.minecraft.network.login.server.SLoginSuccessPacket;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.util.CryptManager;
|
||||
import net.minecraft.util.DefaultUncaughtExceptionHandler;
|
||||
@ -40,7 +47,7 @@ import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Mixin(ServerLoginNetHandler.class)
|
||||
public abstract class ServerLoginNetHandler1Mixin {
|
||||
public abstract class ServerLoginNetHandlerMixin implements ServerLoginNetHandlerBridge {
|
||||
|
||||
// @formatter:off
|
||||
@Shadow private ServerLoginNetHandler.State currentLoginState;
|
||||
@ -54,8 +61,21 @@ public abstract class ServerLoginNetHandler1Mixin {
|
||||
@Shadow protected abstract GameProfile getOfflineProfile(GameProfile original);
|
||||
@Shadow public abstract void disconnect(ITextComponent reason);
|
||||
@Shadow public abstract String getConnectionInfo();
|
||||
@Shadow private ServerPlayerEntity player;
|
||||
// @formatter:on
|
||||
|
||||
public String hostname;
|
||||
|
||||
@Override
|
||||
public String bridge$getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
public void disconnect(final String s) {
|
||||
try {
|
||||
final ITextComponent ichatbasecomponent = new StringTextComponent(s);
|
||||
@ -67,6 +87,40 @@ public abstract class ServerLoginNetHandler1Mixin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
public void tryAcceptPlayer() {
|
||||
/*
|
||||
if (!this.loginGameProfile.isComplete()) {
|
||||
this.loginGameProfile = this.getOfflineProfile(this.loginGameProfile);
|
||||
}
|
||||
*/
|
||||
|
||||
ServerPlayerEntity entity = ((PlayerListBridge) this.server.getPlayerList()).bridge$canPlayerLogin(this.networkManager.getRemoteAddress(), this.loginGameProfile, (ServerLoginNetHandler) (Object) this);
|
||||
if (entity == null) {
|
||||
// this.disconnect(itextcomponent);
|
||||
} else {
|
||||
this.currentLoginState = ServerLoginNetHandler.State.ACCEPTED;
|
||||
if (this.server.getNetworkCompressionThreshold() >= 0 && !this.networkManager.isLocalChannel()) {
|
||||
this.networkManager.sendPacket(new SEnableCompressionPacket(this.server.getNetworkCompressionThreshold()), (p_210149_1_) -> {
|
||||
this.networkManager.setCompressionThreshold(this.server.getNetworkCompressionThreshold());
|
||||
});
|
||||
}
|
||||
|
||||
this.networkManager.sendPacket(new SLoginSuccessPacket(this.loginGameProfile));
|
||||
ServerPlayerEntity serverplayerentity = this.server.getPlayerList().getPlayerByUUID(this.loginGameProfile.getId());
|
||||
if (serverplayerentity != null) {
|
||||
this.currentLoginState = ServerLoginNetHandler.State.DELAY_ACCEPT;
|
||||
this.player = entity;
|
||||
} else {
|
||||
this.server.getPlayerList().initializeConnectionToPlayer(this.networkManager, entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
* @reason
|
||||
@ -102,8 +156,20 @@ public abstract class ServerLoginNetHandler1Mixin {
|
||||
}
|
||||
|
||||
public void initUUID() {
|
||||
UUID uuid = PlayerEntity.getOfflineUUID(this.loginGameProfile.getName());
|
||||
UUID uuid;
|
||||
if (((NetworkManagerBridge) this.networkManager).bridge$getSpoofedUUID() != null) {
|
||||
uuid = ((NetworkManagerBridge) this.networkManager).bridge$getSpoofedUUID();
|
||||
} else {
|
||||
uuid = PlayerEntity.getOfflineUUID(this.loginGameProfile.getName());
|
||||
}
|
||||
this.loginGameProfile = new GameProfile(uuid, this.loginGameProfile.getName());
|
||||
if (((NetworkManagerBridge) this.networkManager).bridge$getSpoofedProfile() != null) {
|
||||
Property[] spoofedProfile;
|
||||
for (int length = (spoofedProfile = ((NetworkManagerBridge) this.networkManager).bridge$getSpoofedProfile()).length, i = 0; i < length; ++i) {
|
||||
final Property property = spoofedProfile[i];
|
||||
this.loginGameProfile.getProperties().put(property.getName(), property);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,7 +197,7 @@ public abstract class ServerLoginNetHandler1Mixin {
|
||||
|
||||
try {
|
||||
String s = (new BigInteger(CryptManager.getServerIdHash("", server.getKeyPair().getPublic(), secretKey))).toString(16);
|
||||
loginGameProfile = server.getMinecraftSessionService().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s, this.getAddress());
|
||||
loginGameProfile = server.getMinecraftSessionService().hasJoinedServer(new GameProfile(null, gameprofile.getName()), s, this.getAddress());
|
||||
if (loginGameProfile != null) {
|
||||
if (!networkManager.isChannelOpen()) {
|
||||
return;
|
||||
@ -143,7 +209,7 @@ public abstract class ServerLoginNetHandler1Mixin {
|
||||
currentLoginState = ServerLoginNetHandler.State.NEGOTIATING;
|
||||
} else {
|
||||
disconnect(new TranslationTextComponent("multiplayer.disconnect.unverified_username"));
|
||||
LOGGER.error("Username '{}' tried to join with an invalid session", (Object) gameprofile.getName());
|
||||
LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName());
|
||||
}
|
||||
} catch (Exception var3) {
|
||||
if (server.isSinglePlayer()) {
|
||||
@ -5,21 +5,20 @@ import com.mojang.authlib.GameProfile;
|
||||
import io.izzel.arclight.api.ArclightVersion;
|
||||
import io.izzel.arclight.common.bridge.entity.player.PlayerEntityBridge;
|
||||
import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge;
|
||||
import io.izzel.arclight.common.bridge.network.play.ServerPlayNetHandlerBridge;
|
||||
import io.izzel.arclight.common.bridge.server.MinecraftServerBridge;
|
||||
import io.izzel.arclight.common.bridge.server.management.PlayerListBridge;
|
||||
import io.izzel.arclight.common.bridge.world.WorldBridge;
|
||||
import io.izzel.arclight.common.bridge.world.dimension.DimensionTypeBridge;
|
||||
import io.izzel.arclight.common.mod.ArclightMod;
|
||||
import io.izzel.arclight.common.mod.server.BukkitRegistry;
|
||||
import io.izzel.arclight.common.mod.util.ArclightCaptures;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.network.IPacket;
|
||||
import net.minecraft.network.NetworkManager;
|
||||
import net.minecraft.network.login.ServerLoginNetHandler;
|
||||
import net.minecraft.network.play.server.SChangeGameStatePacket;
|
||||
import net.minecraft.network.play.server.SChatPacket;
|
||||
import net.minecraft.network.play.server.SEntityStatusPacket;
|
||||
import net.minecraft.network.play.server.SRespawnPacket;
|
||||
import net.minecraft.network.play.server.SServerDifficultyPacket;
|
||||
import net.minecraft.network.play.server.SSetExperiencePacket;
|
||||
import net.minecraft.network.play.server.SSpawnPositionPacket;
|
||||
@ -60,6 +59,7 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.spigotmc.SpigotConfig;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
@ -70,9 +70,12 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import io.izzel.arclight.common.mod.ArclightMod;
|
||||
import io.izzel.arclight.common.mod.util.ArclightCaptures;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -128,10 +131,10 @@ public abstract class PlayerListMixin implements PlayerListBridge {
|
||||
t.printStackTrace();
|
||||
}
|
||||
try {
|
||||
ArclightMod.LOGGER.info("Registering for bukkit... ");
|
||||
ArclightMod.LOGGER.info("registry.begin");
|
||||
BukkitRegistry.registerAll();
|
||||
} catch (Throwable t) {
|
||||
ArclightMod.LOGGER.error("Error handling Forge registries ", t);
|
||||
ArclightMod.LOGGER.error("registry.error", t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,16 +157,11 @@ public abstract class PlayerListMixin implements PlayerListBridge {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
@Nullable
|
||||
public ITextComponent canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile) {
|
||||
final UUID uuid = PlayerEntity.getUUID(gameProfile);
|
||||
final List<ServerPlayerEntity> list = Lists.newArrayList();
|
||||
for (final ServerPlayerEntity entityplayer : this.players) {
|
||||
@Override
|
||||
public ServerPlayerEntity bridge$canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile, ServerLoginNetHandler handler) {
|
||||
UUID uuid = PlayerEntity.getUUID(gameProfile);
|
||||
List<ServerPlayerEntity> list = Lists.newArrayList();
|
||||
for (ServerPlayerEntity entityplayer : this.players) {
|
||||
if (entityplayer.getUniqueID().equals(uuid)) {
|
||||
list.add(entityplayer);
|
||||
}
|
||||
@ -172,36 +170,40 @@ public abstract class PlayerListMixin implements PlayerListBridge {
|
||||
this.writePlayerData(entityplayer);
|
||||
entityplayer.connection.disconnect(new TranslationTextComponent("multiplayer.disconnect.duplicate_login"));
|
||||
}
|
||||
final ServerPlayerEntity entity = new ServerPlayerEntity(this.server, this.server.getWorld(DimensionType.OVERWORLD), gameProfile, new PlayerInteractionManager(this.server.getWorld(DimensionType.OVERWORLD)));
|
||||
final Player player = ((ServerPlayerEntityBridge) entity).bridge$getBukkitEntity();
|
||||
// todo hostname
|
||||
final PlayerLoginEvent event = new PlayerLoginEvent(player, "", ((InetSocketAddress) socketAddress).getAddress());
|
||||
ServerPlayerEntity entity = new ServerPlayerEntity(this.server, this.server.getWorld(DimensionType.OVERWORLD), gameProfile, new PlayerInteractionManager(this.server.getWorld(DimensionType.OVERWORLD)));
|
||||
Player player = ((ServerPlayerEntityBridge) entity).bridge$getBukkitEntity();
|
||||
|
||||
String hostname = handler == null ? "" : ((ServerLoginNetHandlerBridge) handler).bridge$getHostname();
|
||||
InetAddress realAddress = handler == null ? ((InetSocketAddress) socketAddress).getAddress() : ((InetSocketAddress) ((NetworkManagerBridge) handler.networkManager).bridge$getRawAddress()).getAddress();
|
||||
|
||||
PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((InetSocketAddress) socketAddress).getAddress(), realAddress);
|
||||
if (this.getBannedPlayers().isBanned(gameProfile) && !this.getBannedPlayers().getEntry(gameProfile).hasBanExpired()) {
|
||||
final ProfileBanEntry gameprofilebanentry = this.bannedPlayers.getEntry(gameProfile);
|
||||
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned.reason", gameprofilebanentry.getBanReason());
|
||||
ProfileBanEntry gameprofilebanentry = this.bannedPlayers.getEntry(gameProfile);
|
||||
TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned.reason", gameprofilebanentry.getBanReason());
|
||||
if (gameprofilebanentry.getBanEndDate() != null) {
|
||||
chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned.expiration", DATE_FORMAT.format(gameprofilebanentry.getBanEndDate())));
|
||||
}
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage));
|
||||
} else if (!this.canJoin(gameProfile)) {
|
||||
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.not_whitelisted");
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, CraftChatMessage.fromComponent(chatmessage));
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, SpigotConfig.whitelistMessage);
|
||||
} else if (this.getBannedIPs().isBanned(socketAddress) && !this.getBannedIPs().getBanEntry(socketAddress).hasBanExpired()) {
|
||||
final IPBanEntry ipbanentry = this.bannedIPs.getBanEntry(socketAddress);
|
||||
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned_ip.reason", ipbanentry.getBanReason());
|
||||
IPBanEntry ipbanentry = this.bannedIPs.getBanEntry(socketAddress);
|
||||
TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned_ip.reason", ipbanentry.getBanReason());
|
||||
if (ipbanentry.getBanEndDate() != null) {
|
||||
chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned_ip.expiration", DATE_FORMAT.format(ipbanentry.getBanEndDate())));
|
||||
}
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage));
|
||||
} else if (this.players.size() >= this.maxPlayers && !this.bypassesPlayerLimit(gameProfile)) {
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_FULL, "The server is full");
|
||||
event.disallow(PlayerLoginEvent.Result.KICK_FULL, SpigotConfig.serverFullMessage);
|
||||
}
|
||||
this.cserver.getPluginManager().callEvent(event);
|
||||
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
|
||||
return CraftChatMessage.fromStringOrNull(event.getKickMessage());
|
||||
} else {
|
||||
if (handler != null) {
|
||||
handler.disconnect(CraftChatMessage.fromStringOrNull(event.getKickMessage()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public ServerPlayerEntity moveToWorld(ServerPlayerEntity playerIn, DimensionType dimension, boolean conqueredEnd, Location location, boolean avoidSuffocation) {
|
||||
@ -401,7 +403,7 @@ public abstract class PlayerListMixin implements PlayerListBridge {
|
||||
@Inject(method = "playerLoggedOut", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/management/PlayerList;writePlayerData(Lnet/minecraft/entity/player/ServerPlayerEntity;)V"))
|
||||
public void arclight$playerQuitPre(ServerPlayerEntity playerIn, CallbackInfo ci) {
|
||||
CraftEventFactory.handleInventoryCloseEvent(playerIn);
|
||||
PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(playerIn), "\u00A7e" + playerIn.getName() + " left the game");
|
||||
PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(playerIn), "\u00A7e" + playerIn.getName().getFormattedText() + " left the game");
|
||||
cserver.getPluginManager().callEvent(playerQuitEvent);
|
||||
((ServerPlayerEntityBridge) playerIn).bridge$getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
|
||||
playerIn.playerTick();
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
package io.izzel.arclight.common.mod;
|
||||
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ArclightConfig {
|
||||
|
||||
public static ArclightConfig INSTANCE;
|
||||
|
||||
public Optimization optimizations;
|
||||
|
||||
static void init(Path path) {
|
||||
ArclightConnector.LOGGER.info("Loading configurations from {}", path);
|
||||
try {
|
||||
if (!Files.exists(path)) {
|
||||
InputStream stream = ArclightConfig.class.getResourceAsStream("/arclight.yml");
|
||||
Files.copy(stream, path);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ArclightConnector.LOGGER.error("Failed to save default configurations", e);
|
||||
}
|
||||
try {
|
||||
Yaml yaml = new Yaml(new Constructor(ArclightConfig.class));
|
||||
Object load = yaml.load(Files.newInputStream(path));
|
||||
INSTANCE = (ArclightConfig) load;
|
||||
} catch (IOException e) {
|
||||
ArclightConnector.LOGGER.error("Failed to load configurations", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Optimization {
|
||||
|
||||
public boolean removeStreams = true;
|
||||
|
||||
}
|
||||
}
|
||||
@ -2,18 +2,19 @@ package io.izzel.arclight.common.mod;
|
||||
|
||||
import cpw.mods.modlauncher.api.ITransformingClassLoader;
|
||||
import io.izzel.arclight.api.ArclightVersion;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import cpw.mods.modlauncher.api.ITransformingClassLoader;
|
||||
import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger;
|
||||
import io.izzel.arclight.i18n.ArclightConfig;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Mixins;
|
||||
import org.spongepowered.asm.mixin.connect.IMixinConnector;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ArclightConnector implements IMixinConnector {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Arclight");
|
||||
public static final Logger LOGGER = ArclightI18nLogger.getLogger("Arclight");
|
||||
private static final List<String> FILTER_PACKAGE = Arrays.asList("com.google.common", "com.google.gson", "ninja.leaping.configurate", "io.izzel.arclight.api");
|
||||
|
||||
@Override
|
||||
@ -27,7 +28,6 @@ public class ArclightConnector implements IMixinConnector {
|
||||
if (ArclightVersion.atLeast(ArclightVersion.v1_15)) {
|
||||
Mixins.addConfiguration("mixins.arclight.core.1_15.json");
|
||||
}
|
||||
LOGGER.info("Arclight core mixin added.");
|
||||
ArclightConfig.init(Paths.get("arclight.yml"));
|
||||
LOGGER.info("mixin-load.core");
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
@ -14,8 +12,6 @@ import java.util.Map;
|
||||
|
||||
public class ArclightLocator extends AbstractJarFileLocator {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger("Arclight");
|
||||
|
||||
private final IModFile arclight;
|
||||
|
||||
public ArclightLocator() {
|
||||
@ -39,6 +35,5 @@ public class ArclightLocator extends AbstractJarFileLocator {
|
||||
|
||||
@Override
|
||||
public void initArguments(Map<String, ?> arguments) {
|
||||
LOGGER.info("Arclight locator loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
package io.izzel.arclight.common.mod;
|
||||
|
||||
import io.izzel.arclight.common.mod.server.event.ArclightEventDispatcherRegistry;
|
||||
import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger;
|
||||
import net.minecraftforge.fml.CrashReportExtender;
|
||||
import net.minecraftforge.fml.ExtensionPoint;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.network.FMLNetworkConstants;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.craftbukkit.v.CraftCrashReport;
|
||||
|
||||
@Mod("arclight")
|
||||
public class ArclightMod {
|
||||
|
||||
public static final Logger LOGGER = LogManager.getLogger("Arclight");
|
||||
public static final Logger LOGGER = ArclightI18nLogger.getLogger("Arclight");
|
||||
|
||||
public ArclightMod() {
|
||||
LOGGER.info("Arclight Mod loaded.");
|
||||
LOGGER.info("mod-load");
|
||||
ArclightEventDispatcherRegistry.registerAllEventDispatchers();
|
||||
CrashReportExtender.registerCrashCallable("Arclight", () -> new CraftCrashReport().call().toString());
|
||||
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));
|
||||
|
||||
@ -2,6 +2,11 @@ package io.izzel.arclight.common.mod.server;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.izzel.arclight.api.EnumHelper;
|
||||
import io.izzel.arclight.api.Unsafe;
|
||||
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
|
||||
import io.izzel.arclight.common.mod.ArclightMod;
|
||||
import io.izzel.arclight.common.mod.util.potion.ArclightPotionEffect;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.potion.Effect;
|
||||
@ -15,11 +20,6 @@ import org.bukkit.craftbukkit.v.util.CraftMagicNumbers;
|
||||
import org.bukkit.craftbukkit.v.util.CraftNamespacedKey;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
|
||||
import io.izzel.arclight.common.mod.ArclightMod;
|
||||
import io.izzel.arclight.common.mod.util.potion.ArclightPotionEffect;
|
||||
import io.izzel.arclight.api.EnumHelper;
|
||||
import io.izzel.arclight.api.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
@ -31,12 +31,12 @@ import java.util.Set;
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
public class BukkitRegistry {
|
||||
|
||||
private static List<Class<?>> MAT_CTOR = ImmutableList.of(int.class, int.class, int.class);
|
||||
private static Map<String, Material> BY_NAME = getStatic(Material.class, "BY_NAME");
|
||||
private static Map<Block, Material> BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL");
|
||||
private static Map<Item, Material> ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL");
|
||||
private static Map<Material, Item> MATERIAL_ITEM = getStatic(CraftMagicNumbers.class, "MATERIAL_ITEM");
|
||||
private static Map<Material, Block> MATERIAL_BLOCK = getStatic(CraftMagicNumbers.class, "MATERIAL_BLOCK");
|
||||
private static final List<Class<?>> MAT_CTOR = ImmutableList.of(int.class, int.class, int.class);
|
||||
private static final Map<String, Material> BY_NAME = getStatic(Material.class, "BY_NAME");
|
||||
private static final Map<Block, Material> BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL");
|
||||
private static final Map<Item, Material> ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL");
|
||||
private static final Map<Material, Item> MATERIAL_ITEM = getStatic(CraftMagicNumbers.class, "MATERIAL_ITEM");
|
||||
private static final Map<Material, Block> MATERIAL_BLOCK = getStatic(CraftMagicNumbers.class, "MATERIAL_BLOCK");
|
||||
|
||||
public static void registerAll() {
|
||||
loadMaterials();
|
||||
@ -52,7 +52,7 @@ public class BukkitRegistry {
|
||||
Enchantment.registerEnchantment(new CraftEnchantment(entry.getValue()));
|
||||
}
|
||||
Enchantment.stopAcceptingRegistrations();
|
||||
ArclightMod.LOGGER.info("Registered {} new enchantments", size - origin);
|
||||
ArclightMod.LOGGER.info("registry.enchantment", size - origin);
|
||||
}
|
||||
|
||||
private static void loadPotions() {
|
||||
@ -68,7 +68,7 @@ public class BukkitRegistry {
|
||||
ArclightMod.LOGGER.debug("Registered {}: {} as potion", entry.getKey(), effect);
|
||||
}
|
||||
PotionEffectType.stopAcceptingRegistrations();
|
||||
ArclightMod.LOGGER.info("Registered {} new potion effect types", size - origin);
|
||||
ArclightMod.LOGGER.info("registry.potion", size - origin);
|
||||
}
|
||||
|
||||
private static void loadMaterials() {
|
||||
@ -122,7 +122,7 @@ public class BukkitRegistry {
|
||||
ITEM_MATERIAL.put(item, material);
|
||||
MATERIAL_ITEM.put(material, item);
|
||||
}
|
||||
ArclightMod.LOGGER.info("Registered {} new materials, with {} blocks and {} items", i - origin, blocks, items);
|
||||
ArclightMod.LOGGER.info("registry.material", i - origin, blocks, items);
|
||||
}
|
||||
|
||||
private static String toName(ResourceLocation location) {
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
package io.izzel.arclight.common.mod.server.event;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import io.izzel.arclight.common.mod.ArclightMod;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
public abstract class ArclightEventDispatcherRegistry {
|
||||
|
||||
public static void registerAllEventDispatchers() {
|
||||
ArclightMod.LOGGER.info("Arclight register all event dispatchers.");
|
||||
MinecraftForge.EVENT_BUS.register(new BlockBreakEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new BlockPlaceEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new EntityPotionEffectEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new EntityRegainHealthEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new WorldEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new EntityEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new NetworkEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new EntityTeleportEventDispatcher());
|
||||
MinecraftForge.EVENT_BUS.register(new ItemEntityEventDispatcher());
|
||||
ArclightMod.LOGGER.info("registry.forge-event");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
package io.izzel.arclight.common.mod.server.event;
|
||||
|
||||
import io.izzel.arclight.common.bridge.entity.EntityBridge;
|
||||
import io.izzel.arclight.common.bridge.world.WorldBridge;
|
||||
import net.minecraftforge.event.entity.living.LivingHealEvent;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.entity.EntityRegainHealthEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
|
||||
public class EntityRegainHealthEventDispatcher {
|
||||
|
||||
@SubscribeEvent(receiveCanceled = true)
|
||||
public void onLivingHeal(LivingHealEvent event) {
|
||||
EntityRegainHealthEvent bukkitEvent = ArclightEventFactory.callEntityRegainHealthEvent(((EntityBridge) event.getEntity()).bridge$getBukkitEntity(),
|
||||
event.getAmount(), EntityRegainHealthEvent.RegainReason.CUSTOM);
|
||||
event.setAmount((float) bukkitEvent.getAmount());
|
||||
if (bukkitEvent.isCancelled()) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldLoad(WorldEvent.Load event) {
|
||||
Bukkit.getPluginManager().callEvent(new WorldLoadEvent(((WorldBridge) event.getWorld()).bridge$getWorld()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package io.izzel.arclight.common.mod.server.event;
|
||||
|
||||
import io.izzel.arclight.common.bridge.world.WorldBridge;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
|
||||
public class WorldEventDispatcher {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onWorldLoad(WorldEvent.Load event) {
|
||||
Bukkit.getPluginManager().callEvent(new WorldLoadEvent(((WorldBridge) event.getWorld()).bridge$getWorld()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,113 @@
|
||||
package io.izzel.arclight.common.mod.util.log;
|
||||
|
||||
import io.izzel.arclight.i18n.ArclightLocale;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.spi.ExtendedLogger;
|
||||
import org.apache.logging.log4j.spi.ExtendedLoggerWrapper;
|
||||
import org.apache.logging.log4j.util.MessageSupplier;
|
||||
import org.apache.logging.log4j.util.Supplier;
|
||||
|
||||
public class ArclightI18nLogger extends ExtendedLoggerWrapper {
|
||||
|
||||
public ArclightI18nLogger(ExtendedLogger logger) {
|
||||
super(logger, logger.getName(), logger.getMessageFactory());
|
||||
}
|
||||
|
||||
public static Logger getLogger(String name) {
|
||||
return new ArclightI18nLogger((ExtendedLogger) LogManager.getLogger(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, CharSequence message, Throwable t) {
|
||||
this.info("", "");
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message.toString()), t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, Object message, Throwable t) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message.toString()), t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, MessageSupplier msgSupplier, Throwable t) {
|
||||
super.logMessage(fqcn, level, marker, msgSupplier, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, Supplier<?> msgSupplier, Throwable t) {
|
||||
super.logMessage(fqcn, level, marker, msgSupplier, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Throwable t) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), t);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object... params) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4, p5);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4, p5, p6);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4, p5, p6, p7);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4, p5, p6, p7, p8);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Object p0, Object p1, Object p2, Object p3, Object p4, Object p5, Object p6, Object p7, Object p8, Object p9) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void logMessage(String fqcn, Level level, Marker marker, String message, Supplier<?>... paramSuppliers) {
|
||||
super.logMessage(fqcn, level, marker, ArclightLocale.getInstance().get(message), paramSuppliers);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package io.izzel.arclight.common.mod.util.log;
|
||||
|
||||
import java.util.Enumeration;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ArclightLazyLogManager extends LogManager {
|
||||
|
||||
private volatile LogManager delegate;
|
||||
|
||||
@Override
|
||||
public boolean addLogger(Logger logger) {
|
||||
tryGet();
|
||||
if (delegate != null) return delegate.addLogger(logger);
|
||||
return super.addLogger(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger(String name) {
|
||||
tryGet();
|
||||
if (delegate != null) return delegate.getLogger(name);
|
||||
return super.getLogger(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getLoggerNames() {
|
||||
tryGet();
|
||||
if (delegate != null) return delegate.getLoggerNames();
|
||||
return super.getLoggerNames();
|
||||
}
|
||||
|
||||
private void tryGet() {
|
||||
if (delegate != null) return;
|
||||
try {
|
||||
Class<?> name = Class.forName("org.apache.logging.log4j.jul.LogManager");
|
||||
delegate = (LogManager) name.newInstance();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package io.izzel.arclight.common.mod.util;
|
||||
package io.izzel.arclight.common.mod.util.log;
|
||||
|
||||
import org.apache.logging.log4j.jul.ApiLogger;
|
||||
import org.apache.logging.log4j.jul.CoreLoggerAdapter;
|
||||
@ -1,4 +1,4 @@
|
||||
package io.izzel.arclight.common.mod.util;
|
||||
package io.izzel.arclight.common.mod.util.log;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginLogger;
|
||||
@ -18,6 +18,7 @@ public net.minecraft.util.math.shapes.DoubleCubeMergingList <init>(II)V
|
||||
public net.minecraft.block.ComposterBlock$EmptyInventory
|
||||
public net.minecraft.tileentity.SkullTileEntity field_184299_k #sessionService
|
||||
public net.minecraft.item.crafting.Ingredient <init>(Ljava/util/stream/Stream;)V
|
||||
public net.minecraft.world.server.TicketManager field_219377_e #tickets
|
||||
# Arclight 1.15
|
||||
public net.minecraft.entity.Entity field_70165_t #posX
|
||||
public net.minecraft.entity.Entity field_70163_u #posY
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
############################################################################
|
||||
# Arclight configurations
|
||||
#
|
||||
# Repository https://github.com/IzzelAliz/Arclight
|
||||
# Issue Tracker https://github.com/IzzelAliz/Arclight/issues
|
||||
#
|
||||
############################################################################
|
||||
|
||||
optimizations:
|
||||
# The stream allocates useless garbage and hurts a lot of performance
|
||||
# Arclight optimizes this by removing them
|
||||
removeStreams: true
|
||||
@ -21,10 +21,13 @@
|
||||
<TerminalConsole name="Console">
|
||||
<PatternLayout>
|
||||
<!-- use white for info output -->
|
||||
<LoggerNamePatternSelector noConsoleNoAnsi="true" defaultPattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [%c{1.}/%markerSimpleName]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}">
|
||||
<LoggerNamePatternSelector
|
||||
defaultPattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [%c{1.}/%markerSimpleName]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}">
|
||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||
<PatternMatch key="net.minecraft." pattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}"/>
|
||||
<PatternMatch key="com.mojang." pattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}"/>
|
||||
<PatternMatch key="net.minecraft."
|
||||
pattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}"/>
|
||||
<PatternMatch key="com.mojang."
|
||||
pattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}"/>
|
||||
</LoggerNamePatternSelector>
|
||||
</PatternLayout>
|
||||
</TerminalConsole>
|
||||
|
||||
@ -284,9 +284,12 @@
|
||||
"item.crafting.StonecuttingRecipeMixin",
|
||||
"item.crafting.SuspiciousStewRecipeMixin",
|
||||
"item.crafting.TippedArrowRecipeMixin",
|
||||
"network.NetworkManagerMixin",
|
||||
"network.PacketThreadUtilMixin",
|
||||
"network.datasync.EntityDataManagerMixin",
|
||||
"network.login.ServerLoginNetHandler1Mixin",
|
||||
"network.handshake.ServerHandshakeNetHandlerMixin",
|
||||
"network.handshake.client.CHandshakePacketMixin",
|
||||
"network.login.ServerLoginNetHandlerMixin",
|
||||
"network.play.ServerPlayNetHandlerMixin",
|
||||
"network.play.client.CChatMessagePacketMixin",
|
||||
"network.play.client.CCloseWindowPacketMixin",
|
||||
|
||||
@ -68,9 +68,11 @@ def embedLibs = ['org.spongepowered:mixin:0.8', 'org.ow2.asm:asm-util:6.2',
|
||||
dependencies {
|
||||
minecraft "net.minecraftforge:forge:$minecraftVersion-$forgeVersion"
|
||||
compile group: 'org.jetbrains', name: 'annotations', version: '19.0.0'
|
||||
compile project(':arclight-common')
|
||||
compile(project(':arclight-common')) {
|
||||
exclude module: 'forge'
|
||||
}
|
||||
embed project(':i18n-config')
|
||||
embed project(':forge-installer')
|
||||
for (def lib : embedLibs) {
|
||||
arclight lib
|
||||
@ -98,8 +100,9 @@ jar {
|
||||
manifest.attributes 'MixinConnector': 'io.izzel.arclight.impl.ArclightConnector_1_14'
|
||||
manifest.attributes 'Main-Class': 'io.izzel.arclight.server.Main'
|
||||
manifest.attributes 'Implementation-Title': 'Arclight'
|
||||
manifest.attributes 'Implementation-Version': "arclight-${project.version}-${getGitHash()}"
|
||||
manifest.attributes 'Implementation-Version': "arclight-$minecraftVersion-${project.version}-${getGitHash()}"
|
||||
manifest.attributes 'Implementation-Vendor': 'Arclight Team'
|
||||
manifest.attributes 'Implementation-Timestamp': new Date().format("yyyy-MM-dd HH:mm:ss")
|
||||
from(configurations.embed.collect { it.isDirectory() ? it : zipTree(it) }) {
|
||||
exclude "META-INF/MANIFEST.MF"
|
||||
exclude "META-INF/*.SF"
|
||||
|
||||
@ -3,16 +3,22 @@ package io.izzel.arclight.server;
|
||||
import io.izzel.arclight.api.ArclightVersion;
|
||||
import io.izzel.arclight.api.EnumHelper;
|
||||
import io.izzel.arclight.api.Unsafe;
|
||||
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
||||
import io.izzel.arclight.forgeinstaller.ForgeInstaller;
|
||||
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
|
||||
import net.minecraftforge.server.ServerMain;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
System.setProperty("java.util.logging.manager", ArclightLazyLogManager.class.getCanonicalName());
|
||||
System.setProperty("log4j.jul.LoggerAdapter", "io.izzel.arclight.common.mod.util.log.ArclightLoggerAdapter");
|
||||
ArclightLocale.info("i18n.using-language", ArclightConfig.spec().getLocale().getCurrent(), ArclightConfig.spec().getLocale().getFallback());
|
||||
ArclightVersion.setVersion(ArclightVersion.v1_14);
|
||||
ForgeInstaller.install();
|
||||
try { // Java 9 & Java 兼容性
|
||||
int javaVersion = (int) Float.parseFloat(System.getProperty("java.class.version"));
|
||||
@ -26,10 +32,8 @@ public class Main {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager");
|
||||
System.setProperty("log4j.jul.LoggerAdapter", "io.izzel.arclight.common.mod.util.ArclightLoggerAdapter");
|
||||
ArclightVersion.setVersion(ArclightVersion.v1_14);
|
||||
LogManager.getLogger("Arclight").info("Loading mappings ...");
|
||||
printLogo();
|
||||
ArclightI18nLogger.getLogger("Arclight").info("loading-mapping");
|
||||
Objects.requireNonNull(ArclightRemapper.INSTANCE);
|
||||
ServerMain.main(args);
|
||||
} catch (Exception e) {
|
||||
@ -37,4 +41,14 @@ public class Main {
|
||||
System.err.println("Fail to launch Arclight.");
|
||||
}
|
||||
}
|
||||
|
||||
private static void printLogo() throws Exception {
|
||||
try (InputStream stream = Main.class.getResourceAsStream("/META-INF/MANIFEST.MF")) {
|
||||
Manifest manifest = new Manifest(stream);
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
String version = attributes.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
|
||||
String buildTime = attributes.getValue("Implementation-Timestamp");
|
||||
ArclightI18nLogger.getLogger("Arclight").info("logo", version, buildTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
i18n-config/build.gradle
Normal file
19
i18n-config/build.gradle
Normal file
@ -0,0 +1,19 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven {
|
||||
name = 'sponge'
|
||||
url = 'https://repo.spongepowered.org/maven'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'org.spongepowered:configurate-hocon:3.6.1'
|
||||
}
|
||||
|
||||
jar {
|
||||
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) })
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package io.izzel.arclight.i18n;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import io.izzel.arclight.i18n.conf.ConfigSpec;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
|
||||
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
|
||||
import ninja.leaping.configurate.objectmapping.ObjectMappingException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class ArclightConfig {
|
||||
|
||||
private static ArclightConfig instance;
|
||||
|
||||
private final CommentedConfigurationNode node;
|
||||
private final ConfigSpec spec;
|
||||
|
||||
public ArclightConfig(CommentedConfigurationNode node) throws ObjectMappingException {
|
||||
this.node = node;
|
||||
this.spec = this.node.getValue(TypeToken.of(ConfigSpec.class));
|
||||
}
|
||||
|
||||
public CommentedConfigurationNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public ConfigSpec getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public ConfigurationNode get(String path) {
|
||||
return this.node.getNode((Object[]) path.split("\\."));
|
||||
}
|
||||
|
||||
public static ConfigSpec spec() {
|
||||
return instance.spec;
|
||||
}
|
||||
|
||||
private static void load() throws Exception {
|
||||
Path path = Paths.get("arclight.conf");
|
||||
CommentedConfigurationNode node = HoconConfigurationLoader.builder().setSource(
|
||||
() -> new BufferedReader(new InputStreamReader(ArclightConfig.class.getResourceAsStream("/META-INF/arclight.conf"), StandardCharsets.UTF_8))
|
||||
).build().load();
|
||||
HoconConfigurationLoader loader = HoconConfigurationLoader.builder().setPath(path).build();
|
||||
CommentedConfigurationNode cur = loader.load();
|
||||
cur.mergeValuesFrom(node);
|
||||
cur.getNode("locale", "current").setValue(ArclightLocale.getInstance().getCurrent());
|
||||
fillComments(cur, ArclightLocale.getInstance());
|
||||
instance = new ArclightConfig(cur);
|
||||
loader.save(cur);
|
||||
}
|
||||
|
||||
private static void fillComments(CommentedConfigurationNode node, ArclightLocale locale) {
|
||||
if (!node.getComment().isPresent()) {
|
||||
String path = pathOf(node);
|
||||
Optional<String> option = locale.getOption("comments." + path + ".comment");
|
||||
option.ifPresent(node::setComment);
|
||||
}
|
||||
if (node.hasMapChildren()) {
|
||||
for (CommentedConfigurationNode value : node.getChildrenMap().values()) {
|
||||
fillComments(value, locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String pathOf(ConfigurationNode node) {
|
||||
StringJoiner joiner = new StringJoiner(".");
|
||||
for (Object o : node.getPath()) {
|
||||
if (o != null) {
|
||||
joiner.add(o.toString());
|
||||
}
|
||||
}
|
||||
String s = joiner.toString();
|
||||
return s.isEmpty() ? "__root__" : s;
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
load();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,132 @@
|
||||
package io.izzel.arclight.i18n;
|
||||
|
||||
import ninja.leaping.configurate.ValueType;
|
||||
import ninja.leaping.configurate.commented.CommentedConfigurationNode;
|
||||
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
public class ArclightLocale {
|
||||
|
||||
private static ArclightLocale instance;
|
||||
|
||||
private final String current, fallback;
|
||||
private final CommentedConfigurationNode node;
|
||||
|
||||
public ArclightLocale(String current, String fallback, CommentedConfigurationNode node) {
|
||||
this.current = current;
|
||||
this.fallback = fallback;
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public String getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public String getFallback() {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
public CommentedConfigurationNode getNode() {
|
||||
return node;
|
||||
}
|
||||
|
||||
public String format(String node, Object... args) {
|
||||
return MessageFormat.format(get(node), args);
|
||||
}
|
||||
|
||||
public String get(String path) {
|
||||
return getOption(path).orElse(path);
|
||||
}
|
||||
|
||||
public Optional<String> getOption(String path) {
|
||||
CommentedConfigurationNode node = this.node.getNode((Object[]) path.split("\\."));
|
||||
if (node.getValueType() == ValueType.LIST) {
|
||||
StringJoiner joiner = new StringJoiner("\n");
|
||||
for (CommentedConfigurationNode configurationNode : node.getChildrenList()) {
|
||||
joiner.add(configurationNode.getString());
|
||||
}
|
||||
return Optional.ofNullable(joiner.toString());
|
||||
} else {
|
||||
return Optional.ofNullable(node.getString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void info(String path, Object... args) {
|
||||
System.out.println(instance.format(path, args));
|
||||
}
|
||||
|
||||
public static void error(String path, Object... args) {
|
||||
System.err.println(instance.format(path, args));
|
||||
}
|
||||
|
||||
public static ArclightLocale getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static void init() throws Exception {
|
||||
Map.Entry<String, String> entry = getLocale();
|
||||
String current = entry.getKey();
|
||||
String fallback = entry.getValue();
|
||||
InputStream stream = ArclightLocale.class.getResourceAsStream("/META-INF/i18n/" + fallback + ".conf");
|
||||
if (stream == null) throw new RuntimeException("Fallback locale is not found: " + fallback);
|
||||
CommentedConfigurationNode node = HoconConfigurationLoader.builder().setSource(localeSource(fallback)).build().load();
|
||||
instance = new ArclightLocale(current, fallback, node);
|
||||
if (!current.equals(fallback)) {
|
||||
try {
|
||||
CommentedConfigurationNode curNode = HoconConfigurationLoader.builder().setSource(localeSource(current)).build().load();
|
||||
curNode.mergeValuesFrom(node);
|
||||
instance = new ArclightLocale(current, fallback, curNode);
|
||||
} catch (Exception e) {
|
||||
System.err.println(instance.format("i18n.current-not-available", current));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Callable<BufferedReader> localeSource(String path) {
|
||||
return () -> new BufferedReader(new InputStreamReader(ArclightLocale.class.getResourceAsStream("/META-INF/i18n/" + path + ".conf"), StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static Map.Entry<String, String> getLocale() {
|
||||
try {
|
||||
Path path = Paths.get("arclight.conf");
|
||||
if (!Files.exists(path)) {
|
||||
throw new Exception();
|
||||
} else {
|
||||
CommentedConfigurationNode node = HoconConfigurationLoader.builder().setPath(path).build().load();
|
||||
CommentedConfigurationNode locale = node.getNode("locale");
|
||||
String current = locale.getNode("current").getString(currentLocale());
|
||||
String fallback = locale.getNode("fallback").getString("zh_cn");
|
||||
return new AbstractMap.SimpleImmutableEntry<>(current, fallback);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
return new AbstractMap.SimpleImmutableEntry<>(currentLocale(), "zh_cn");
|
||||
}
|
||||
}
|
||||
|
||||
private static String currentLocale() {
|
||||
Locale locale = Locale.getDefault();
|
||||
return locale.getLanguage().toLowerCase(Locale.ROOT) + "_" + locale.getCountry().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
init();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package io.izzel.arclight.i18n.conf;
|
||||
|
||||
import ninja.leaping.configurate.objectmapping.Setting;
|
||||
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
|
||||
|
||||
@ConfigSerializable
|
||||
public class ConfigSpec {
|
||||
|
||||
@Setting("_v")
|
||||
private int version;
|
||||
|
||||
@Setting("optimization")
|
||||
private OptimizationSpec optimizationSpec;
|
||||
|
||||
@Setting("locale")
|
||||
private LocaleSpec localeSpec;
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public OptimizationSpec getOptimization() {
|
||||
return optimizationSpec;
|
||||
}
|
||||
|
||||
public LocaleSpec getLocale() {
|
||||
return localeSpec;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package io.izzel.arclight.i18n.conf;
|
||||
|
||||
import ninja.leaping.configurate.objectmapping.Setting;
|
||||
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
|
||||
|
||||
@ConfigSerializable
|
||||
public class LocaleSpec {
|
||||
|
||||
@Setting("current")
|
||||
private String current;
|
||||
|
||||
@Setting("fallback")
|
||||
private String fallback;
|
||||
|
||||
public String getCurrent() {
|
||||
return current;
|
||||
}
|
||||
|
||||
public String getFallback() {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package io.izzel.arclight.i18n.conf;
|
||||
|
||||
import ninja.leaping.configurate.objectmapping.Setting;
|
||||
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
|
||||
|
||||
@ConfigSerializable
|
||||
public class OptimizationSpec {
|
||||
|
||||
@Setting("remove-stream")
|
||||
private boolean removeStream;
|
||||
|
||||
public boolean isRemoveStream() {
|
||||
return removeStream;
|
||||
}
|
||||
}
|
||||
8
i18n-config/src/main/resources/META-INF/arclight.conf
Normal file
8
i18n-config/src/main/resources/META-INF/arclight.conf
Normal file
@ -0,0 +1,8 @@
|
||||
_v = 1
|
||||
|
||||
locale {
|
||||
fallback = "zh_cn"
|
||||
}
|
||||
optimization {
|
||||
remove-stream = true
|
||||
}
|
||||
44
i18n-config/src/main/resources/META-INF/i18n/en_us.conf
Normal file
44
i18n-config/src/main/resources/META-INF/i18n/en_us.conf
Normal file
@ -0,0 +1,44 @@
|
||||
logo = [
|
||||
""
|
||||
""
|
||||
" §1 ___ §9 §3 §6___§e §6 __ /\\"
|
||||
" §1 / |§9 ____§3____§6/ §e(_)§6__ / / / /"
|
||||
" §1 / /| |§9/ __§3/ __§6/ §e/ /§6 _ / _ \\/__/"
|
||||
" §1 / ___ §9/ / §3/ /_§6/ §e/ /§6 / / // / /"
|
||||
" §1/_/ |§9/_/ §3\\__§6/_§e/_/§6\\_ /_//_/ /"
|
||||
" §1 §9 §3 §6 §e §6/__/ \\/"
|
||||
""
|
||||
" §aVersion {}"
|
||||
" §aBuild Date {}"
|
||||
""
|
||||
]
|
||||
|
||||
i18n {
|
||||
current-not-available = "Current locale {0} is not available"
|
||||
using-language = "Using locale {0} and fallback locale {1}"
|
||||
}
|
||||
loading-mapping = "Loading mappings ..."
|
||||
mixin-load {
|
||||
core = "Arclight core mixin added."
|
||||
optimization = "Arclight optimization mixin added."
|
||||
}
|
||||
mod-load = "Arclight Mod loaded."
|
||||
registry {
|
||||
forge-event = "Arclight events registered."
|
||||
begin = "Regitring for Bukkit ..."
|
||||
error = "Error occured registring Forge "
|
||||
enchantment = "Registered {} enchantments"
|
||||
potion = "Registered {} new potion effect types"
|
||||
material = "Registered {} new materials with {} blocks and {} items"
|
||||
}
|
||||
|
||||
comments {
|
||||
_v.comment = [
|
||||
"Repository: https://github.com/IzzelAliz/Arclight"
|
||||
"Issue Tracker: https://github.com/IzzelAliz/Arclight/issues"
|
||||
""
|
||||
""
|
||||
"Config version number, do not edit."
|
||||
]
|
||||
locale.comment = "Language/I18n settings"
|
||||
}
|
||||
45
i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf
Normal file
45
i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf
Normal file
@ -0,0 +1,45 @@
|
||||
logo = [
|
||||
""
|
||||
""
|
||||
" §1 ___ §9 §3 §6___§e §6 __ /\\"
|
||||
" §1 / |§9 ____§3____§6/ §e(_)§6__ / / / /"
|
||||
" §1 / /| |§9/ __§3/ __§6/ §e/ /§6 _ / _ \\/__/"
|
||||
" §1 / ___ §9/ / §3/ /_§6/ §e/ /§6 / / // / /"
|
||||
" §1/_/ |§9/_/ §3\\__§6/_§e/_/§6\\_ /_//_/ /"
|
||||
" §1 §9 §3 §6 §e §6/__/ \\/"
|
||||
""
|
||||
" §a运行版本 {}"
|
||||
" §a构建日期 {}"
|
||||
""
|
||||
]
|
||||
|
||||
i18n {
|
||||
current-not-available = "选择的语言 {0} 不可用"
|
||||
using-language = "正在使用 {0} 语言,{1} 作为备选语言"
|
||||
}
|
||||
loading-mapping = "正在加载混淆数据 ..."
|
||||
mixin-load {
|
||||
core = "核心 Mixin 配置已加载"
|
||||
optimization = "服务端优化 Mixin 配置已加载"
|
||||
}
|
||||
mod-load = "Arclight Mod 已加载"
|
||||
registry {
|
||||
forge-event = "Arclight 事件系统已注册"
|
||||
begin = "正在向 Bukkit 注册 ..."
|
||||
error = "处理 Forge 注册时出错 "
|
||||
enchantment = "注册了 {} 个新的附魔"
|
||||
potion = "注册了 {} 个新的药水效果"
|
||||
material = "注册了 {} 个材料,其中 {} 个方块 {} 个物品"
|
||||
}
|
||||
|
||||
comments {
|
||||
_v.comment = [
|
||||
"源代码仓库: https://github.com/IzzelAliz/Arclight"
|
||||
"提交反馈/错误报告: https://github.com/IzzelAliz/Arclight/issues"
|
||||
""
|
||||
""
|
||||
"配置文件版本号,请勿编辑"
|
||||
]
|
||||
locale.comment = "语言/国际化相关设置"
|
||||
optimization.comment = "服务端优化相关设置"
|
||||
}
|
||||
@ -5,4 +5,5 @@ include 'arclight-common'
|
||||
include 'forge-installer'
|
||||
include 'arclight-api'
|
||||
include 'arclight-forge-1.15'
|
||||
include 'i18n-config'
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user