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:
IzzelAliz 2020-06-15 19:40:19 +08:00
commit c538046c1a
40 changed files with 975 additions and 161 deletions

View File

@ -9,7 +9,7 @@ A Bukkit server implementation utilizing Mixin.
## Installing ## 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). 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 ## Support
@ -19,7 +19,18 @@ QQ Group chat 3556966
## Contributing ## 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 ## License

View File

@ -60,6 +60,7 @@ dependencies {
compile 'mysql:mysql-connector-java:5.1.47' compile 'mysql:mysql-connector-java:5.1.47'
compile 'org.yaml:snakeyaml:1.23' compile 'org.yaml:snakeyaml:1.23'
compile project(':arclight-api') compile project(':arclight-api')
compile project(':i18n-config')
} }
remapSpigotJar { remapSpigotJar {

View File

@ -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();
}

View File

@ -0,0 +1,8 @@
package io.izzel.arclight.common.bridge.network.login;
public interface ServerLoginNetHandlerBridge {
String bridge$getHostname();
void bridge$setHostname(String hostname);
}

View File

@ -1,6 +1,7 @@
package io.izzel.arclight.common.bridge.server.management; package io.izzel.arclight.common.bridge.server.management;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.play.server.SRespawnPacket; import net.minecraft.network.play.server.SRespawnPacket;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@ -8,8 +9,10 @@ import net.minecraft.world.GameType;
import net.minecraft.world.WorldType; import net.minecraft.world.WorldType;
import net.minecraft.world.dimension.DimensionType; import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.server.ServerWorld; import net.minecraft.world.server.ServerWorld;
import net.minecraft.network.login.ServerLoginNetHandler;
import org.bukkit.craftbukkit.v.CraftServer; import org.bukkit.craftbukkit.v.CraftServer;
import java.net.SocketAddress;
import java.util.List; import java.util.List;
public interface PlayerListBridge { public interface PlayerListBridge {
@ -20,6 +23,8 @@ public interface PlayerListBridge {
CraftServer bridge$getCraftServer(); CraftServer bridge$getCraftServer();
ServerPlayerEntity bridge$canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile, ServerLoginNetHandler handler);
boolean bridge$worldNoCollision(ServerWorld world, Entity entity); boolean bridge$worldNoCollision(ServerWorld world, Entity entity);
void bridge$setSpawnPoint(ServerPlayerEntity player, BlockPos pos, boolean flag, DimensionType type, boolean flag1); void bridge$setSpawnPoint(ServerPlayerEntity player, BlockPos pos, boolean flag, DimensionType type, boolean flag1);

View File

@ -1,12 +1,12 @@
package io.izzel.arclight.common.mixin.bukkit; 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.Plugin;
import org.bukkit.plugin.PluginLogger; import org.bukkit.plugin.PluginLogger;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import io.izzel.arclight.common.mod.util.ArclightPluginLogger;
@Mixin(JavaPlugin.class) @Mixin(JavaPlugin.class)
public class JavaPluginMixin { public class JavaPluginMixin {

View File

@ -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")) @Redirect(method = "heal", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/LivingEntity;setHealth(F)V"))
public void arclight$healEvent(LivingEntity livingEntity, float health) { 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 f = this.getHealth();
float amount = health - f; 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) { if (this.valid) {
Bukkit.getPluginManager().callEvent(event); Bukkit.getPluginManager().callEvent(event);
} }

View File

@ -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();
}
}

View File

@ -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());
}
}
}
}

View File

@ -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);
}
}

View File

@ -1,14 +1,21 @@
package io.izzel.arclight.common.mixin.core.network.login; package io.izzel.arclight.common.mixin.core.network.login;
import com.mojang.authlib.GameProfile; 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.MinecraftServerBridge;
import io.izzel.arclight.common.bridge.server.management.PlayerListBridge;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.network.login.ServerLoginNetHandler; import net.minecraft.network.login.ServerLoginNetHandler;
import net.minecraft.network.login.client.CEncryptionResponsePacket; import net.minecraft.network.login.client.CEncryptionResponsePacket;
import net.minecraft.network.login.client.CLoginStartPacket; import net.minecraft.network.login.client.CLoginStartPacket;
import net.minecraft.network.login.server.SDisconnectLoginPacket; 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.SEncryptionRequestPacket;
import net.minecraft.network.login.server.SLoginSuccessPacket;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.util.CryptManager; import net.minecraft.util.CryptManager;
import net.minecraft.util.DefaultUncaughtExceptionHandler; import net.minecraft.util.DefaultUncaughtExceptionHandler;
@ -40,7 +47,7 @@ import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@Mixin(ServerLoginNetHandler.class) @Mixin(ServerLoginNetHandler.class)
public abstract class ServerLoginNetHandler1Mixin { public abstract class ServerLoginNetHandlerMixin implements ServerLoginNetHandlerBridge {
// @formatter:off // @formatter:off
@Shadow private ServerLoginNetHandler.State currentLoginState; @Shadow private ServerLoginNetHandler.State currentLoginState;
@ -54,8 +61,21 @@ public abstract class ServerLoginNetHandler1Mixin {
@Shadow protected abstract GameProfile getOfflineProfile(GameProfile original); @Shadow protected abstract GameProfile getOfflineProfile(GameProfile original);
@Shadow public abstract void disconnect(ITextComponent reason); @Shadow public abstract void disconnect(ITextComponent reason);
@Shadow public abstract String getConnectionInfo(); @Shadow public abstract String getConnectionInfo();
@Shadow private ServerPlayerEntity player;
// @formatter:on // @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) { public void disconnect(final String s) {
try { try {
final ITextComponent ichatbasecomponent = new StringTextComponent(s); 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 * @author IzzelAliz
* @reason * @reason
@ -102,8 +156,20 @@ public abstract class ServerLoginNetHandler1Mixin {
} }
public void initUUID() { 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()); 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 { try {
String s = (new BigInteger(CryptManager.getServerIdHash("", server.getKeyPair().getPublic(), secretKey))).toString(16); 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 (loginGameProfile != null) {
if (!networkManager.isChannelOpen()) { if (!networkManager.isChannelOpen()) {
return; return;
@ -143,7 +209,7 @@ public abstract class ServerLoginNetHandler1Mixin {
currentLoginState = ServerLoginNetHandler.State.NEGOTIATING; currentLoginState = ServerLoginNetHandler.State.NEGOTIATING;
} else { } else {
disconnect(new TranslationTextComponent("multiplayer.disconnect.unverified_username")); 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) { } catch (Exception var3) {
if (server.isSinglePlayer()) { if (server.isSinglePlayer()) {

View File

@ -5,21 +5,20 @@ import com.mojang.authlib.GameProfile;
import io.izzel.arclight.api.ArclightVersion; import io.izzel.arclight.api.ArclightVersion;
import io.izzel.arclight.common.bridge.entity.player.PlayerEntityBridge; import io.izzel.arclight.common.bridge.entity.player.PlayerEntityBridge;
import io.izzel.arclight.common.bridge.entity.player.ServerPlayerEntityBridge; 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.MinecraftServerBridge;
import io.izzel.arclight.common.bridge.server.management.PlayerListBridge; import io.izzel.arclight.common.bridge.server.management.PlayerListBridge;
import io.izzel.arclight.common.bridge.world.WorldBridge; 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.server.BukkitRegistry;
import io.izzel.arclight.common.mod.util.ArclightCaptures; import io.izzel.arclight.common.mod.util.ArclightCaptures;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.IPacket; import net.minecraft.network.IPacket;
import net.minecraft.network.NetworkManager; import net.minecraft.network.NetworkManager;
import net.minecraft.network.login.ServerLoginNetHandler;
import net.minecraft.network.play.server.SChangeGameStatePacket; import net.minecraft.network.play.server.SChangeGameStatePacket;
import net.minecraft.network.play.server.SChatPacket; import net.minecraft.network.play.server.SChatPacket;
import net.minecraft.network.play.server.SEntityStatusPacket; 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.SServerDifficultyPacket;
import net.minecraft.network.play.server.SSetExperiencePacket; import net.minecraft.network.play.server.SSetExperiencePacket;
import net.minecraft.network.play.server.SSpawnPositionPacket; 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.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerRespawnEvent;
import org.spigotmc.SpigotConfig;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable; 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.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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 javax.annotation.Nullable;
import java.io.File; import java.io.File;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -128,10 +131,10 @@ public abstract class PlayerListMixin implements PlayerListBridge {
t.printStackTrace(); t.printStackTrace();
} }
try { try {
ArclightMod.LOGGER.info("Registering for bukkit... "); ArclightMod.LOGGER.info("registry.begin");
BukkitRegistry.registerAll(); BukkitRegistry.registerAll();
} catch (Throwable t) { } 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 {
} }
} }
/** @Override
* @author IzzelAliz public ServerPlayerEntity bridge$canPlayerLogin(SocketAddress socketAddress, GameProfile gameProfile, ServerLoginNetHandler handler) {
* @reason UUID uuid = PlayerEntity.getUUID(gameProfile);
*/ List<ServerPlayerEntity> list = Lists.newArrayList();
@Overwrite for (ServerPlayerEntity entityplayer : this.players) {
@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) {
if (entityplayer.getUniqueID().equals(uuid)) { if (entityplayer.getUniqueID().equals(uuid)) {
list.add(entityplayer); list.add(entityplayer);
} }
@ -172,36 +170,40 @@ public abstract class PlayerListMixin implements PlayerListBridge {
this.writePlayerData(entityplayer); this.writePlayerData(entityplayer);
entityplayer.connection.disconnect(new TranslationTextComponent("multiplayer.disconnect.duplicate_login")); 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))); 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(); Player player = ((ServerPlayerEntityBridge) entity).bridge$getBukkitEntity();
// todo hostname
final PlayerLoginEvent event = new PlayerLoginEvent(player, "", ((InetSocketAddress) socketAddress).getAddress()); 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()) { if (this.getBannedPlayers().isBanned(gameProfile) && !this.getBannedPlayers().getEntry(gameProfile).hasBanExpired()) {
final ProfileBanEntry gameprofilebanentry = this.bannedPlayers.getEntry(gameProfile); ProfileBanEntry gameprofilebanentry = this.bannedPlayers.getEntry(gameProfile);
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned.reason", gameprofilebanentry.getBanReason()); TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned.reason", gameprofilebanentry.getBanReason());
if (gameprofilebanentry.getBanEndDate() != null) { if (gameprofilebanentry.getBanEndDate() != null) {
chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned.expiration", DATE_FORMAT.format(gameprofilebanentry.getBanEndDate()))); chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned.expiration", DATE_FORMAT.format(gameprofilebanentry.getBanEndDate())));
} }
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage)); event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage));
} else if (!this.canJoin(gameProfile)) { } else if (!this.canJoin(gameProfile)) {
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.not_whitelisted"); event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, SpigotConfig.whitelistMessage);
event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, CraftChatMessage.fromComponent(chatmessage));
} else if (this.getBannedIPs().isBanned(socketAddress) && !this.getBannedIPs().getBanEntry(socketAddress).hasBanExpired()) { } else if (this.getBannedIPs().isBanned(socketAddress) && !this.getBannedIPs().getBanEntry(socketAddress).hasBanExpired()) {
final IPBanEntry ipbanentry = this.bannedIPs.getBanEntry(socketAddress); IPBanEntry ipbanentry = this.bannedIPs.getBanEntry(socketAddress);
final TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned_ip.reason", ipbanentry.getBanReason()); TranslationTextComponent chatmessage = new TranslationTextComponent("multiplayer.disconnect.banned_ip.reason", ipbanentry.getBanReason());
if (ipbanentry.getBanEndDate() != null) { if (ipbanentry.getBanEndDate() != null) {
chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned_ip.expiration", DATE_FORMAT.format(ipbanentry.getBanEndDate()))); chatmessage.appendSibling(new TranslationTextComponent("multiplayer.disconnect.banned_ip.expiration", DATE_FORMAT.format(ipbanentry.getBanEndDate())));
} }
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage)); event.disallow(PlayerLoginEvent.Result.KICK_BANNED, CraftChatMessage.fromComponent(chatmessage));
} else if (this.players.size() >= this.maxPlayers && !this.bypassesPlayerLimit(gameProfile)) { } 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); this.cserver.getPluginManager().callEvent(event);
if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
return CraftChatMessage.fromStringOrNull(event.getKickMessage()); if (handler != null) {
} else { handler.disconnect(CraftChatMessage.fromStringOrNull(event.getKickMessage()));
}
return null; return null;
} }
return entity;
} }
public ServerPlayerEntity moveToWorld(ServerPlayerEntity playerIn, DimensionType dimension, boolean conqueredEnd, Location location, boolean avoidSuffocation) { 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")) @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) { public void arclight$playerQuitPre(ServerPlayerEntity playerIn, CallbackInfo ci) {
CraftEventFactory.handleInventoryCloseEvent(playerIn); 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); cserver.getPluginManager().callEvent(playerQuitEvent);
((ServerPlayerEntityBridge) playerIn).bridge$getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); ((ServerPlayerEntityBridge) playerIn).bridge$getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
playerIn.playerTick(); playerIn.playerTick();

View File

@ -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;
}
}

View File

@ -2,18 +2,19 @@ package io.izzel.arclight.common.mod;
import cpw.mods.modlauncher.api.ITransformingClassLoader; import cpw.mods.modlauncher.api.ITransformingClassLoader;
import io.izzel.arclight.api.ArclightVersion; 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.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixins; import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.connect.IMixinConnector; import org.spongepowered.asm.mixin.connect.IMixinConnector;
import java.nio.file.Paths;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class ArclightConnector implements IMixinConnector { 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"); private static final List<String> FILTER_PACKAGE = Arrays.asList("com.google.common", "com.google.gson", "ninja.leaping.configurate", "io.izzel.arclight.api");
@Override @Override
@ -27,7 +28,6 @@ public class ArclightConnector implements IMixinConnector {
if (ArclightVersion.atLeast(ArclightVersion.v1_15)) { if (ArclightVersion.atLeast(ArclightVersion.v1_15)) {
Mixins.addConfiguration("mixins.arclight.core.1_15.json"); Mixins.addConfiguration("mixins.arclight.core.1_15.json");
} }
LOGGER.info("Arclight core mixin added."); LOGGER.info("mixin-load.core");
ArclightConfig.init(Paths.get("arclight.yml"));
} }
} }

View File

@ -4,8 +4,6 @@ import com.google.common.collect.ImmutableList;
import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator; import net.minecraftforge.fml.loading.moddiscovery.AbstractJarFileLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile; import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.forgespi.locating.IModFile; import net.minecraftforge.forgespi.locating.IModFile;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File; import java.io.File;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -14,8 +12,6 @@ import java.util.Map;
public class ArclightLocator extends AbstractJarFileLocator { public class ArclightLocator extends AbstractJarFileLocator {
private static final Logger LOGGER = LogManager.getLogger("Arclight");
private final IModFile arclight; private final IModFile arclight;
public ArclightLocator() { public ArclightLocator() {
@ -39,6 +35,5 @@ public class ArclightLocator extends AbstractJarFileLocator {
@Override @Override
public void initArguments(Map<String, ?> arguments) { public void initArguments(Map<String, ?> arguments) {
LOGGER.info("Arclight locator loaded.");
} }
} }

View File

@ -1,23 +1,23 @@
package io.izzel.arclight.common.mod; package io.izzel.arclight.common.mod;
import io.izzel.arclight.common.mod.server.event.ArclightEventDispatcherRegistry; 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.CrashReportExtender;
import net.minecraftforge.fml.ExtensionPoint; import net.minecraftforge.fml.ExtensionPoint;
import net.minecraftforge.fml.ModLoadingContext; import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.network.FMLNetworkConstants; import net.minecraftforge.fml.network.FMLNetworkConstants;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.craftbukkit.v.CraftCrashReport; import org.bukkit.craftbukkit.v.CraftCrashReport;
@Mod("arclight") @Mod("arclight")
public class ArclightMod { public class ArclightMod {
public static final Logger LOGGER = LogManager.getLogger("Arclight"); public static final Logger LOGGER = ArclightI18nLogger.getLogger("Arclight");
public ArclightMod() { public ArclightMod() {
LOGGER.info("Arclight Mod loaded."); LOGGER.info("mod-load");
ArclightEventDispatcherRegistry.registerAllEventDispatchers(); ArclightEventDispatcherRegistry.registerAllEventDispatchers();
CrashReportExtender.registerCrashCallable("Arclight", () -> new CraftCrashReport().call().toString()); CrashReportExtender.registerCrashCallable("Arclight", () -> new CraftCrashReport().call().toString());
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true)); ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.DISPLAYTEST, () -> Pair.of(() -> FMLNetworkConstants.IGNORESERVERONLY, (a, b) -> true));

View File

@ -2,6 +2,11 @@ package io.izzel.arclight.common.mod.server;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; 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.block.Block;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.potion.Effect; 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.craftbukkit.v.util.CraftNamespacedKey;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.potion.PotionEffectType; 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.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
@ -31,12 +31,12 @@ import java.util.Set;
@SuppressWarnings({"unchecked", "ConstantConditions"}) @SuppressWarnings({"unchecked", "ConstantConditions"})
public class BukkitRegistry { public class BukkitRegistry {
private static List<Class<?>> MAT_CTOR = ImmutableList.of(int.class, int.class, int.class); private static final 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 final Map<String, Material> BY_NAME = getStatic(Material.class, "BY_NAME");
private static Map<Block, Material> BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL"); private static final Map<Block, Material> BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL");
private static Map<Item, Material> ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL"); private static final Map<Item, Material> ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL");
private static Map<Material, Item> MATERIAL_ITEM = getStatic(CraftMagicNumbers.class, "MATERIAL_ITEM"); private static final 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 Map<Material, Block> MATERIAL_BLOCK = getStatic(CraftMagicNumbers.class, "MATERIAL_BLOCK");
public static void registerAll() { public static void registerAll() {
loadMaterials(); loadMaterials();
@ -52,7 +52,7 @@ public class BukkitRegistry {
Enchantment.registerEnchantment(new CraftEnchantment(entry.getValue())); Enchantment.registerEnchantment(new CraftEnchantment(entry.getValue()));
} }
Enchantment.stopAcceptingRegistrations(); Enchantment.stopAcceptingRegistrations();
ArclightMod.LOGGER.info("Registered {} new enchantments", size - origin); ArclightMod.LOGGER.info("registry.enchantment", size - origin);
} }
private static void loadPotions() { private static void loadPotions() {
@ -68,7 +68,7 @@ public class BukkitRegistry {
ArclightMod.LOGGER.debug("Registered {}: {} as potion", entry.getKey(), effect); ArclightMod.LOGGER.debug("Registered {}: {} as potion", entry.getKey(), effect);
} }
PotionEffectType.stopAcceptingRegistrations(); PotionEffectType.stopAcceptingRegistrations();
ArclightMod.LOGGER.info("Registered {} new potion effect types", size - origin); ArclightMod.LOGGER.info("registry.potion", size - origin);
} }
private static void loadMaterials() { private static void loadMaterials() {
@ -122,7 +122,7 @@ public class BukkitRegistry {
ITEM_MATERIAL.put(item, material); ITEM_MATERIAL.put(item, material);
MATERIAL_ITEM.put(material, item); 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) { private static String toName(ResourceLocation location) {

View File

@ -1,20 +1,20 @@
package io.izzel.arclight.common.mod.server.event; package io.izzel.arclight.common.mod.server.event;
import net.minecraftforge.common.MinecraftForge;
import io.izzel.arclight.common.mod.ArclightMod; import io.izzel.arclight.common.mod.ArclightMod;
import net.minecraftforge.common.MinecraftForge;
public abstract class ArclightEventDispatcherRegistry { public abstract class ArclightEventDispatcherRegistry {
public static void registerAllEventDispatchers() { public static void registerAllEventDispatchers() {
ArclightMod.LOGGER.info("Arclight register all event dispatchers.");
MinecraftForge.EVENT_BUS.register(new BlockBreakEventDispatcher()); MinecraftForge.EVENT_BUS.register(new BlockBreakEventDispatcher());
MinecraftForge.EVENT_BUS.register(new BlockPlaceEventDispatcher()); MinecraftForge.EVENT_BUS.register(new BlockPlaceEventDispatcher());
MinecraftForge.EVENT_BUS.register(new EntityPotionEffectEventDispatcher()); 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 EntityEventDispatcher());
MinecraftForge.EVENT_BUS.register(new NetworkEventDispatcher()); MinecraftForge.EVENT_BUS.register(new NetworkEventDispatcher());
MinecraftForge.EVENT_BUS.register(new EntityTeleportEventDispatcher()); MinecraftForge.EVENT_BUS.register(new EntityTeleportEventDispatcher());
MinecraftForge.EVENT_BUS.register(new ItemEntityEventDispatcher()); MinecraftForge.EVENT_BUS.register(new ItemEntityEventDispatcher());
ArclightMod.LOGGER.info("registry.forge-event");
} }
} }

View File

@ -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()));
}
}

View File

@ -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()));
}
}

View File

@ -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);
}
}

View File

@ -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) {
}
}
}

View File

@ -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.ApiLogger;
import org.apache.logging.log4j.jul.CoreLoggerAdapter; import org.apache.logging.log4j.jul.CoreLoggerAdapter;

View File

@ -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.Plugin;
import org.bukkit.plugin.PluginLogger; import org.bukkit.plugin.PluginLogger;

View File

@ -18,6 +18,7 @@ public net.minecraft.util.math.shapes.DoubleCubeMergingList <init>(II)V
public net.minecraft.block.ComposterBlock$EmptyInventory public net.minecraft.block.ComposterBlock$EmptyInventory
public net.minecraft.tileentity.SkullTileEntity field_184299_k #sessionService public net.minecraft.tileentity.SkullTileEntity field_184299_k #sessionService
public net.minecraft.item.crafting.Ingredient <init>(Ljava/util/stream/Stream;)V 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 # Arclight 1.15
public net.minecraft.entity.Entity field_70165_t #posX public net.minecraft.entity.Entity field_70165_t #posX
public net.minecraft.entity.Entity field_70163_u #posY public net.minecraft.entity.Entity field_70163_u #posY

View File

@ -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

View File

@ -21,10 +21,13 @@
<TerminalConsole name="Console"> <TerminalConsole name="Console">
<PatternLayout> <PatternLayout>
<!-- use white for info output --> <!-- 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 --> <!-- 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="net.minecraft."
<PatternMatch key="com.mojang." pattern="%highlight{[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg}%n%tEx}{INFO=normal}"/> 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> </LoggerNamePatternSelector>
</PatternLayout> </PatternLayout>
</TerminalConsole> </TerminalConsole>

View File

@ -284,9 +284,12 @@
"item.crafting.StonecuttingRecipeMixin", "item.crafting.StonecuttingRecipeMixin",
"item.crafting.SuspiciousStewRecipeMixin", "item.crafting.SuspiciousStewRecipeMixin",
"item.crafting.TippedArrowRecipeMixin", "item.crafting.TippedArrowRecipeMixin",
"network.NetworkManagerMixin",
"network.PacketThreadUtilMixin", "network.PacketThreadUtilMixin",
"network.datasync.EntityDataManagerMixin", "network.datasync.EntityDataManagerMixin",
"network.login.ServerLoginNetHandler1Mixin", "network.handshake.ServerHandshakeNetHandlerMixin",
"network.handshake.client.CHandshakePacketMixin",
"network.login.ServerLoginNetHandlerMixin",
"network.play.ServerPlayNetHandlerMixin", "network.play.ServerPlayNetHandlerMixin",
"network.play.client.CChatMessagePacketMixin", "network.play.client.CChatMessagePacketMixin",
"network.play.client.CCloseWindowPacketMixin", "network.play.client.CCloseWindowPacketMixin",

View File

@ -68,9 +68,11 @@ def embedLibs = ['org.spongepowered:mixin:0.8', 'org.ow2.asm:asm-util:6.2',
dependencies { dependencies {
minecraft "net.minecraftforge:forge:$minecraftVersion-$forgeVersion" minecraft "net.minecraftforge:forge:$minecraftVersion-$forgeVersion"
compile group: 'org.jetbrains', name: 'annotations', version: '19.0.0' compile group: 'org.jetbrains', name: 'annotations', version: '19.0.0'
compile project(':arclight-common')
compile(project(':arclight-common')) { compile(project(':arclight-common')) {
exclude module: 'forge' exclude module: 'forge'
} }
embed project(':i18n-config')
embed project(':forge-installer') embed project(':forge-installer')
for (def lib : embedLibs) { for (def lib : embedLibs) {
arclight lib arclight lib
@ -98,8 +100,9 @@ jar {
manifest.attributes 'MixinConnector': 'io.izzel.arclight.impl.ArclightConnector_1_14' manifest.attributes 'MixinConnector': 'io.izzel.arclight.impl.ArclightConnector_1_14'
manifest.attributes 'Main-Class': 'io.izzel.arclight.server.Main' manifest.attributes 'Main-Class': 'io.izzel.arclight.server.Main'
manifest.attributes 'Implementation-Title': 'Arclight' 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-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) }) { from(configurations.embed.collect { it.isDirectory() ? it : zipTree(it) }) {
exclude "META-INF/MANIFEST.MF" exclude "META-INF/MANIFEST.MF"
exclude "META-INF/*.SF" exclude "META-INF/*.SF"

View File

@ -3,16 +3,22 @@ package io.izzel.arclight.server;
import io.izzel.arclight.api.ArclightVersion; import io.izzel.arclight.api.ArclightVersion;
import io.izzel.arclight.api.EnumHelper; import io.izzel.arclight.api.EnumHelper;
import io.izzel.arclight.api.Unsafe; 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.forgeinstaller.ForgeInstaller;
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
import net.minecraftforge.server.ServerMain; import net.minecraftforge.server.ServerMain;
import org.apache.logging.log4j.LogManager;
import java.io.InputStream;
import java.util.Objects; import java.util.Objects;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
public class Main { public class Main {
public static void main(String[] args) throws Throwable { 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(); ForgeInstaller.install();
try { // Java 9 & Java 兼容性 try { // Java 9 & Java 兼容性
int javaVersion = (int) Float.parseFloat(System.getProperty("java.class.version")); int javaVersion = (int) Float.parseFloat(System.getProperty("java.class.version"));
@ -26,10 +32,8 @@ public class Main {
return; return;
} }
try { try {
System.setProperty("java.util.logging.manager", "org.apache.logging.log4j.jul.LogManager"); printLogo();
System.setProperty("log4j.jul.LoggerAdapter", "io.izzel.arclight.common.mod.util.ArclightLoggerAdapter"); ArclightI18nLogger.getLogger("Arclight").info("loading-mapping");
ArclightVersion.setVersion(ArclightVersion.v1_14);
LogManager.getLogger("Arclight").info("Loading mappings ...");
Objects.requireNonNull(ArclightRemapper.INSTANCE); Objects.requireNonNull(ArclightRemapper.INSTANCE);
ServerMain.main(args); ServerMain.main(args);
} catch (Exception e) { } catch (Exception e) {
@ -37,4 +41,14 @@ public class Main {
System.err.println("Fail to launch Arclight."); 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
View 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) })
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,8 @@
_v = 1
locale {
fallback = "zh_cn"
}
optimization {
remove-stream = true
}

View 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"
}

View 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 = "服务端优化相关设置"
}

View File

@ -5,4 +5,5 @@ include 'arclight-common'
include 'forge-installer' include 'forge-installer'
include 'arclight-api' include 'arclight-api'
include 'arclight-forge-1.15' include 'arclight-forge-1.15'
include 'i18n-config'