makeObfSourceJar = false
replace "@VERSION@", project.version
- replaceIn "com/irtimaled/bbor/install/Main.java"
+ replaceIn "com/irtimaled/bbor/Main.java"
replace "@MC_VERSION@", project.mcVersion
- replaceIn "com/irtimaled/bbor/install/Main.java"
+ replaceIn "com/irtimaled/bbor/Main.java"
}
mixin {
jar {
finalizedBy reobfJar
manifest.attributes(
- 'Main-Class': 'com.irtimaled.bbor.install.Main'
+ 'Main-Class': 'com.irtimaled.bbor.Main'
)
classifier = 'vanilla'
--- /dev/null
+package com.irtimaled.bbor;
+
+import com.irtimaled.bbor.install.Installer;
+import com.irtimaled.bbor.server.ServerRunner;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+public class Main {
+ public static void main(String... args) throws IOException {
+ if (args.length > 0 && args[0].equals("--server")) {
+ ServerRunner.run("@MC_VERSION@", Arrays.asList(args).subList(1, args.length));
+ } else {
+ Installer.install("@VERSION@", "@MC_VERSION@");
+
+ }
+ }
+}
\ No newline at end of file
@Override
protected void setWorldData(long seed, int spawnX, int spawnZ) {
super.setWorldData(seed, spawnX, spawnZ);
- addSpawnChunkBoundingBoxes(spawnX, spawnZ);
+ renderer.setWorldData(seed, spawnX, spawnZ);
}
- private void addSpawnChunkBoundingBoxes(int spawnX, int spawnZ) {
- BoundingBox worldSpawnBoundingBox = getWorldSpawnBoundingBox(spawnX, spawnZ);
- BoundingBox spawnChunksBoundingBox = buildSpawnChunksBoundingBox(spawnX, spawnZ, 12, BoundingBoxType.SpawnChunks);
- BoundingBox lazySpawnChunksBoundingBox = buildSpawnChunksBoundingBox(spawnX, spawnZ, 16, BoundingBoxType.LazySpawnChunks);
- runOnCache(DimensionType.OVERWORLD, cache -> {
- cache.addBoundingBox(worldSpawnBoundingBox);
- cache.addBoundingBox(spawnChunksBoundingBox);
- cache.addBoundingBox(lazySpawnChunksBoundingBox);
- });
- }
-
- private BoundingBox getWorldSpawnBoundingBox(int spawnX, int spawnZ) {
- BlockPos minBlockPos = new BlockPos(spawnX - 10, 0, spawnZ - 10);
- BlockPos maxBlockPos = new BlockPos(spawnX + 10, 0, spawnZ + 10);
-
- return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, BoundingBoxType.WorldSpawn);
- }
-
- private BoundingBox buildSpawnChunksBoundingBox(int spawnX, int spawnZ, int size, BoundingBoxType type) {
- double midOffset = CHUNK_SIZE * (size / 2.0);
- double midX = Math.round((float) (spawnX / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
- double midZ = Math.round((float) (spawnZ / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
- BlockPos minBlockPos = new BlockPos(midX - midOffset, 0, midZ - midOffset);
- if (spawnX / (double) CHUNK_SIZE % 0.5D == 0.0D && spawnZ / (double) CHUNK_SIZE % 0.5D == 0.0D) {
- midX += (double) CHUNK_SIZE;
- midZ += (double) CHUNK_SIZE;
- }
- BlockPos maxBlockPos = new BlockPos(midX + midOffset, 0, midZ + midOffset);
- return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, type);
- }
}
import com.irtimaled.bbor.client.renderers.*;
import com.irtimaled.bbor.common.BoundingBoxCache;
+import com.irtimaled.bbor.common.BoundingBoxType;
import com.irtimaled.bbor.common.models.*;
import com.irtimaled.bbor.config.ConfigManager;
import net.minecraft.client.Minecraft;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.dimension.DimensionType;
import org.lwjgl.opengl.GL11;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import static com.irtimaled.bbor.client.Constants.CHUNK_SIZE;
public class ClientRenderer {
private final GetCache getCache;
private static final Map<Class<? extends BoundingBox>, Renderer> boundingBoxRendererMap = new HashMap<>();
+ private long seed;
+ private Set<BoundingBox> spawnChunkBoundingBoxes = new HashSet<>();
ClientRenderer(GetCache getCache) {
this.getCache = getCache;
minBlockPos.getZ() <= maxZ;
}
+ private boolean isWithinRenderDistance(BoundingBox boundingBox) {
+ return isWithinRenderDistance(boundingBox.getMinBlockPos(), boundingBox.getMaxBlockPos());
+ }
+
public void render(DimensionType dimensionType, Boolean outerBoxesOnly) {
- BoundingBoxCache cache = getCache.apply(dimensionType);
- if (cache == null) return;
+ Map<BoundingBox, Set<BoundingBox>> boundingBoxes = getBoundingBoxes(dimensionType);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glLineWidth(2.0f);
if (ConfigManager.alwaysVisible.get()) {
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
}
- for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : cache.getBoundingBoxes().entrySet()) {
+ for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : boundingBoxes.entrySet()) {
BoundingBox key = entry.getKey();
- if (!key.shouldRender() || !isWithinRenderDistance(key.getMinBlockPos(), key.getMaxBlockPos())) continue;
+ if (!key.shouldRender()) continue;
Renderer renderer = boundingBoxRendererMap.get(key.getClass());
if (renderer == null) continue;
if (!outerBoxesOnly) {
- Set<BoundingBox> boundingBoxes = entry.getValue();
- if (boundingBoxes != null) {
- boundingBoxes.forEach(renderer::render);
+ Set<BoundingBox> children = entry.getValue();
+ if (children != null) {
+ children.forEach(renderer::render);
continue;
}
}
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_TEXTURE_2D);
}
+
+ private Map<BoundingBox, Set<BoundingBox>> getBoundingBoxes(DimensionType dimensionType) {
+ Map<BoundingBox, Set<BoundingBox>> boundingBoxes = new HashMap<>();
+ if (dimensionType == DimensionType.OVERWORLD) {
+ if (BoundingBoxType.SlimeChunks.shouldRender()) {
+ addSlimeChunks(boundingBoxes);
+ }
+
+ for (BoundingBox boundingBox : spawnChunkBoundingBoxes) {
+ if (boundingBox.shouldRender() && isWithinRenderDistance(boundingBox)) {
+ boundingBoxes.put(boundingBox, null);
+ }
+ }
+ }
+
+ BoundingBoxCache cache = getCache.apply(dimensionType);
+ if (cache != null) {
+ for (Map.Entry<BoundingBox, Set<BoundingBox>> entry : cache.getBoundingBoxes().entrySet()) {
+ BoundingBox key = entry.getKey();
+ if (key.shouldRender() && isWithinRenderDistance(key)) {
+ boundingBoxes.put(key, entry.getValue());
+ }
+ }
+ }
+ return boundingBoxes;
+ }
+
+ private void addSlimeChunks(Map<BoundingBox, Set<BoundingBox>> boundingBoxes) {
+ Minecraft minecraft = Minecraft.getInstance();
+ int renderDistanceChunks = minecraft.gameSettings.renderDistanceChunks;
+ int playerChunkX = MathHelper.floor(PlayerData.getX() / 16.0D);
+ int playerChunkZ = MathHelper.floor(PlayerData.getZ() / 16.0D);
+ for (int chunkX = playerChunkX - renderDistanceChunks; chunkX <= playerChunkX + renderDistanceChunks; ++chunkX) {
+ for (int chunkZ = playerChunkZ - renderDistanceChunks; chunkZ <= playerChunkZ + renderDistanceChunks; ++chunkZ) {
+ if (isSlimeChunk(chunkX, chunkZ)) {
+ ChunkPos chunk = new ChunkPos(chunkX, chunkZ);
+ BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
+ BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
+ boundingBoxes.put(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos), null);
+ }
+ }
+ }
+ }
+
+ private boolean isSlimeChunk(int chunkX, int chunkZ) {
+ Random r = new Random(seed +
+ (long) (chunkX * chunkX * 4987142) +
+ (long) (chunkX * 5947611) +
+ (long) (chunkZ * chunkZ) * 4392871L +
+ (long) (chunkZ * 389711) ^ 987234911L);
+ return r.nextInt(10) == 0;
+ }
+
+ void setWorldData(long seed, int spawnX, int spawnZ) {
+ this.seed = seed;
+ spawnChunkBoundingBoxes = getSpawnChunkBoundingBoxes(spawnX, spawnZ);
+ }
+
+ private Set<BoundingBox> getSpawnChunkBoundingBoxes(int spawnX, int spawnZ) {
+ Set<BoundingBox> boundingBoxes = new HashSet<>();
+ boundingBoxes.add(getWorldSpawnBoundingBox(spawnX, spawnZ));
+ boundingBoxes.add(buildSpawnChunksBoundingBox(spawnX, spawnZ, 12, BoundingBoxType.SpawnChunks));
+ boundingBoxes.add(buildSpawnChunksBoundingBox(spawnX, spawnZ, 16, BoundingBoxType.LazySpawnChunks));
+ return boundingBoxes;
+ }
+
+ private BoundingBox getWorldSpawnBoundingBox(int spawnX, int spawnZ) {
+ BlockPos minBlockPos = new BlockPos(spawnX - 10, 0, spawnZ - 10);
+ BlockPos maxBlockPos = new BlockPos(spawnX + 10, 0, spawnZ + 10);
+
+ return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, BoundingBoxType.WorldSpawn);
+ }
+
+ private BoundingBox buildSpawnChunksBoundingBox(int spawnX, int spawnZ, int size, BoundingBoxType type) {
+ double midOffset = CHUNK_SIZE * (size / 2.0);
+ double midX = Math.round((float) (spawnX / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
+ double midZ = Math.round((float) (spawnZ / (double) CHUNK_SIZE)) * (double) CHUNK_SIZE;
+ BlockPos minBlockPos = new BlockPos(midX - midOffset, 0, midZ - midOffset);
+ if (spawnX / (double) CHUNK_SIZE % 0.5D == 0.0D && spawnZ / (double) CHUNK_SIZE % 0.5D == 0.0D) {
+ midX += (double) CHUNK_SIZE;
+ midZ += (double) CHUNK_SIZE;
+ }
+ BlockPos maxBlockPos = new BlockPos(midX + midOffset, 0, midZ + midOffset);
+ return BoundingBoxWorldSpawn.from(minBlockPos, maxBlockPos, type);
+ }
}
import com.irtimaled.bbor.common.models.BoundingBoxVillage;
import com.irtimaled.bbor.common.models.WorldData;
import com.irtimaled.bbor.config.ConfigManager;
+import io.netty.channel.local.LocalAddress;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.network.play.server.SPacketCustomPayload;
import net.minecraft.util.math.BlockPos;
import java.util.function.Consumer;
public class CommonProxy {
- private Map<EntityPlayerMP, DimensionType> playerDimensions = new ConcurrentHashMap<>();
+ private Set<EntityPlayerMP> players = new HashSet<>();
private Map<EntityPlayerMP, Set<BoundingBox>> playerBoundingBoxesCache = new HashMap<>();
private Map<Integer, BoundingBoxVillage> villageCache = new HashMap<>();
private Map<DimensionType, ChunkProcessor> chunkProcessors = new HashMap<>();
EventBus.subscribe(WorldLoaded.class, e -> worldLoaded(e.getWorld()));
EventBus.subscribe(ChunkLoaded.class, e -> chunkLoaded(e.getChunk()));
EventBus.subscribe(MobSpawnerBroken.class, e -> mobSpawnerBroken(e.getDimensionType(), e.getPos()));
- EventBus.subscribe(PlayerChangedDimension.class, e -> playerChangedDimension(e.getPlayer()));
EventBus.subscribe(PlayerLoggedIn.class, e -> playerLoggedIn(e.getPlayer()));
EventBus.subscribe(PlayerLoggedOut.class, e -> playerLoggedOut(e.getPlayer()));
EventBus.subscribe(PlayerSubscribed.class, e -> sendBoundingBoxes(e.getPlayer()));
ChunkProcessor chunkProcessor = null;
if (dimensionType == DimensionType.OVERWORLD) {
setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
- chunkProcessor = new OverworldChunkProcessor(boundingBoxCache, world.getSeed());
+ chunkProcessor = new OverworldChunkProcessor(boundingBoxCache);
}
if (dimensionType == DimensionType.NETHER) {
chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
}
}
- private void playerChangedDimension(EntityPlayerMP player) {
- if (playerDimensions.containsKey(player)) {
- sendBoundingBoxes(player);
- }
- }
-
private void playerLoggedIn(EntityPlayerMP player) {
+ if (player.connection.netManager.getRemoteAddress() instanceof LocalAddress) return;
player.connection.sendPacket(InitializeClient.getPayload(worldData));
}
private void playerLoggedOut(EntityPlayerMP player) {
- playerDimensions.remove(player);
+ players.remove(player);
playerBoundingBoxesCache.remove(player);
}
private void sendRemoveBoundingBox(DimensionType dimensionType, BoundingBox boundingBox) {
SPacketCustomPayload payload = RemoveBoundingBox.getPayload(dimensionType, boundingBox);
- for (EntityPlayerMP player : playerDimensions.keySet()) {
+ if (payload == null) return;
+
+ for (EntityPlayerMP player : players) {
if (DimensionType.getById(player.dimension) == dimensionType) {
- Logger.info("remove 1 entry from %s (%s)", player.getScoreboardName(), dimensionType);
player.connection.sendPacket(payload);
if (playerBoundingBoxesCache.containsKey(player)) {
private void sendBoundingBoxes(EntityPlayerMP player) {
DimensionType dimensionType = DimensionType.getById(player.dimension);
- playerDimensions.put(player, dimensionType);
+ players.add(player);
sendToPlayer(player, getCache(dimensionType));
}
Map<BoundingBox, Set<BoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
DimensionType dimensionType = DimensionType.getById(player.dimension);
- if (cacheSubset.keySet().size() > 0) {
- Logger.info("send %d entries to %s (%s)", cacheSubset.keySet().size(), player.getScoreboardName(), dimensionType);
- }
for (BoundingBox key : cacheSubset.keySet()) {
Set<BoundingBox> boundingBoxes = cacheSubset.get(key);
- player.connection.sendPacket(AddBoundingBox.getPayload(dimensionType, key, boundingBoxes));
+ SPacketCustomPayload payload = AddBoundingBox.getPayload(dimensionType, key, boundingBoxes);
+ if (payload != null)
+ player.connection.sendPacket(payload);
if (!playerBoundingBoxesCache.containsKey(player)) {
playerBoundingBoxesCache.put(player, new HashSet<>());
}
private void tick() {
- for (EntityPlayerMP player : playerDimensions.keySet()) {
- DimensionType dimensionType = playerDimensions.get(player);
+ for (EntityPlayerMP player : players) {
+ DimensionType dimensionType = DimensionType.getById(player.dimension);
sendToPlayer(player, getCache(dimensionType));
}
}
return dimensionCache.get(dimensionType);
}
- protected BoundingBoxCache getOrCreateCache(DimensionType dimensionType)
- {
+ protected BoundingBoxCache getOrCreateCache(DimensionType dimensionType) {
return dimensionCache.computeIfAbsent(dimensionType, dt -> new BoundingBoxCache());
}
private static Map<Class<?>, Consumer<?>> handlers = new HashMap<>();
public static <evt> void publish(evt event) {
+ if (event == null) return;
+
Class clazz = event.getClass();
Consumer<?> handler = handlers.get(clazz);
if (handler == null) return;
import com.irtimaled.bbor.common.BoundingBoxCache;
import com.irtimaled.bbor.common.BoundingBoxType;
-import com.irtimaled.bbor.common.models.BoundingBoxSlimeChunk;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.ChunkPos;
-import net.minecraft.world.chunk.Chunk;
-
-import java.util.Random;
public class OverworldChunkProcessor extends ChunkProcessor {
- private final long seed;
-
- public OverworldChunkProcessor(BoundingBoxCache boundingBoxCache, long seed) {
+ public OverworldChunkProcessor(BoundingBoxCache boundingBoxCache) {
super(boundingBoxCache);
- this.seed = seed;
supportedStructures.add(BoundingBoxType.DesertTemple);
supportedStructures.add(BoundingBoxType.JungleTemple);
supportedStructures.add(BoundingBoxType.WitchHut);
supportedStructures.add(BoundingBoxType.PillagerOutpost);
}
- private boolean isSlimeChunk(int chunkX, int chunkZ) {
- Random r = new Random(seed +
- (long) (chunkX * chunkX * 4987142) +
- (long) (chunkX * 5947611) +
- (long) (chunkZ * chunkZ) * 4392871L +
- (long) (chunkZ * 389711) ^ 987234911L);
- return r.nextInt(10) == 0;
- }
-
- private void addSlimeChunk(ChunkPos chunk) {
- if(!isSlimeChunk(chunk.x, chunk.z)) return;
- BlockPos minBlockPos = new BlockPos(chunk.getXStart(), 1, chunk.getZStart());
- BlockPos maxBlockPos = new BlockPos(chunk.getXEnd(), 38, chunk.getZEnd());
- boundingBoxCache.addBoundingBox(BoundingBoxSlimeChunk.from(minBlockPos, maxBlockPos));
- }
-
- @Override
- public void process(Chunk chunk) {
- super.process(chunk);
- addSlimeChunk(chunk.getPos());
- }
}
+++ /dev/null
-package com.irtimaled.bbor.common.events;
-
-import net.minecraft.entity.player.EntityPlayerMP;
-
-public class PlayerChangedDimension {
- private final EntityPlayerMP player;
-
- public PlayerChangedDimension(EntityPlayerMP player) {
- this.player = player;
- }
-
- public EntityPlayerMP getPlayer() {
- return player;
- }
-}
public static final ResourceLocation NAME = new ResourceLocation("bbor:add_bounding_box");
public static SPacketCustomPayload getPayload(DimensionType dimensionType, BoundingBox key, Set<BoundingBox> boundingBoxes) {
+ if(!BoundingBoxSerializer.canSerialize(key)) return null;
+
PacketBuffer buf = new PacketBuffer(Unpooled.buffer());
buf.writeVarInt(dimensionType.getId());
BoundingBoxSerializer.serialize(key, buf);
public static AddBoundingBoxReceived getEvent(PacketBuffer buf) {
DimensionType dimensionType = DimensionType.getById(buf.readVarInt());
BoundingBox key = BoundingBoxDeserializer.deserialize(buf);
+ if (key == null) return null;
+
Set<BoundingBox> boundingBoxes = new HashSet<>();
while (buf.isReadable()) {
BoundingBox boundingBox = BoundingBoxDeserializer.deserialize(buf);
class BoundingBoxDeserializer {
static BoundingBox deserialize(PacketBuffer buf) {
+ if (!buf.isReadable(2)) return null;
+
char type = buf.readChar();
switch (type) {
case 'V':
private static BoundingBox deserializeStructure(PacketBuffer buf) {
BoundingBoxType type = BoundingBoxType.getByNameHash(buf.readInt());
- if(type == null) return null;
+ if (type == null) return null;
BlockPos minBlockPos = deserializeBlockPos(buf);
BlockPos maxBlockPos = deserializeBlockPos(buf);
return BoundingBoxStructure.from(minBlockPos, maxBlockPos, type);
import net.minecraft.util.math.BlockPos;
import java.awt.*;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.BiConsumer;
class BoundingBoxSerializer {
+ private static final Map<Class, BiConsumer<BoundingBox, PacketBuffer>> serializers = new HashMap<>();
+
+ static {
+ serializers.put(BoundingBoxVillage.class, (bb, buf) -> serializeVillage((BoundingBoxVillage) bb, buf));
+ serializers.put(BoundingBoxStructure.class, (bb, buf) -> serializeStructure((BoundingBoxStructure) bb, buf));
+ serializers.put(BoundingBoxMobSpawner.class, (bb, buf) -> serializeMobSpawner((BoundingBoxMobSpawner) bb, buf));
+ }
+
+ static boolean canSerialize(BoundingBox key) {
+ return serializers.containsKey(key.getClass());
+ }
+
static void serialize(BoundingBox boundingBox, PacketBuffer buf) {
- if (boundingBox instanceof BoundingBoxVillage) {
- serializeVillage((BoundingBoxVillage) boundingBox, buf);
- }
- if (boundingBox instanceof BoundingBoxStructure) {
- serializeStructure((BoundingBoxStructure) boundingBox, buf);
- }
- if (boundingBox instanceof BoundingBoxMobSpawner) {
- serializeMobSpawner((BoundingBoxMobSpawner) boundingBox, buf);
- }
+ BiConsumer<BoundingBox, PacketBuffer> serializer = serializers.get(boundingBox.getClass());
+ if (serializer == null) return;
+
+ serializer.accept(boundingBox, buf);
}
private static void serializeVillage(BoundingBoxVillage boundingBox, PacketBuffer buf) {
public static final ResourceLocation NAME = new ResourceLocation("bbor:remove_bounding_box");
public static SPacketCustomPayload getPayload(DimensionType dimensionType, BoundingBox key) {
+ if(!BoundingBoxSerializer.canSerialize(key)) return null;
+
PacketBuffer buf = new PacketBuffer(Unpooled.buffer());
buf.writeVarInt(dimensionType.getId());
BoundingBoxSerializer.serialize(key, buf);
public static RemoveBoundingBoxReceived getEvent(PacketBuffer buf) {
DimensionType dimensionType = DimensionType.getById(buf.readVarInt());
BoundingBox key = BoundingBoxDeserializer.deserialize(buf);
+ if(key == null) return null;
+
return new RemoveBoundingBoxReceived(dimensionType, key);
}
}
--- /dev/null
+package com.irtimaled.bbor.install;
+
+import javax.swing.*;
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+public class Installer {
+ public static void install(final String version, final String mcVersion) {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+
+ try {
+ String osName = getOsName();
+ File minecraftFolder = getMinecraftFolder(osName);
+ File versionFolder = new File(minecraftFolder, "versions/BBOR-" + version + "/");
+ versionFolder.mkdirs();
+
+ File versionJson = new File(versionFolder, "BBOR-" + version + ".json");
+ Files.copy(Installer.class.getResourceAsStream("/profile.json"), versionJson.toPath(), StandardCopyOption.REPLACE_EXISTING);
+
+ try {
+ File profilesJson = new File(minecraftFolder, "launcher_profiles.json");
+ if (profilesJson.exists()) { // TODO: use gson instead
+ String identifier = "\"bbor-" + mcVersion + "\"";
+ String contents = new String(Files.readAllBytes(profilesJson.toPath()));
+ if (contents.contains(identifier)) {
+ contents = contents.replaceAll(",\n *" + identifier + ": \\{[^}]*},", ",");
+ contents = contents.replaceAll(",?\n *" + identifier + ": \\{[^}]*},?", "");
+ }
+
+ String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
+
+ contents = contents.replace("\n \"profiles\": {", "\n \"profiles\": {\n" +
+ " " + identifier + ": {\n" +
+ " \"name\": \"Bounding Box Outline Reloaded\",\n" +
+ " \"type\": \"custom\",\n" +
+ " \"created\": \"" + date + "T00:00:00.000Z\",\n" +
+ " \"lastUsed\": \"2100-01-01T00:00:00.000Z\",\n" +
+ " \"lastVersionId\": \"BBOR-" + version + "\"\n" +
+ " },");
+
+ Files.write(profilesJson.toPath(), contents.getBytes());
+ }
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+
+ try {
+ String source = Installer.class.getProtectionDomain().getCodeSource().getLocation().getPath();
+ if (source.startsWith("/") && osName.contains("win")) {
+ source = source.substring(1);
+ }
+ File mainJar = new File(minecraftFolder, "libraries/com/irtimaled/bbor/" + version + "/bbor-" + version + ".jar");
+ mainJar.getParentFile().mkdirs();
+ Files.copy(Paths.get(source), mainJar.toPath(), StandardCopyOption.REPLACE_EXISTING);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+
+ JOptionPane.showMessageDialog(null,
+ "Bounding Box Outline Reloaded " + version + " has been successfully installed!\n" +
+ "\n" +
+ "Re-open the Minecraft Launcher to see it in the dropdown.",
+ "Bounding Box Outline Reloaded Installer", JOptionPane.INFORMATION_MESSAGE);
+ } catch (Throwable t) {
+ StringWriter w = new StringWriter();
+ t.printStackTrace(new PrintWriter(w));
+ JOptionPane.showMessageDialog(null,
+ "An error occured while installing Bounding Box Outline Reloaded, please report this to the issue\n" +
+ "tracker (https://github.com/irtimaled/BoundingBoxOutlineReloaded/issues):\n" +
+ "\n" +
+ w.toString().replace("\t", " "), "Bounding Box Outline Reloaded Installer", JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private static File getMinecraftFolder(String osName) {
+ File minecraftFolder;
+ if (osName.contains("win")) {
+ minecraftFolder = new File(System.getenv("APPDATA") + "/.minecraft");
+ } else if (osName.contains("mac")) {
+ minecraftFolder = new File(System.getProperty("user.home") + "/Library/Application Support/minecraft");
+ } else {
+ minecraftFolder = new File(System.getProperty("user.home") + "/.minecraft");
+ }
+ return minecraftFolder;
+ }
+
+ private static String getOsName() {
+ return System.getProperty("os.name").toLowerCase(Locale.ROOT);
+ }
+}
+++ /dev/null
-package com.irtimaled.bbor.install;
-
-import javax.swing.*;
-import java.io.File;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.nio.file.StandardCopyOption;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-public class Main {
- public static void main(String... args) throws Throwable {
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- } catch (Throwable t) {
- t.printStackTrace();
- }
-
- try {
- String osName = getOsName();
- File minecraftFolder = getMinecraftFolder(osName);
- File versionFolder = new File(minecraftFolder, "versions/BBOR-@VERSION@/");
- versionFolder.mkdirs();
-
- File versionJson = new File(versionFolder, "BBOR-@VERSION@.json");
- Files.copy(Main.class.getResourceAsStream("/profile.json"), versionJson.toPath(), StandardCopyOption.REPLACE_EXISTING);
-
- try {
- File profilesJson = new File(minecraftFolder, "launcher_profiles.json");
- if (profilesJson.exists()) { // TODO: use gson instead
- String identifier = "\"bbor-@MC_VERSION@\"";
- String contents = new String(Files.readAllBytes(profilesJson.toPath()));
- if (contents.contains(identifier)) {
- contents = contents.replaceAll(",\n *" + identifier + ": \\{[^}]*},", ",");
- contents = contents.replaceAll(",?\n *" + identifier + ": \\{[^}]*},?", "");
- }
-
- String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
-
- contents = contents.replace("\n \"profiles\": {", "\n \"profiles\": {\n" +
- " " + identifier + ": {\n" +
- " \"name\": \"Bounding Box Outline Reloaded\",\n" +
- " \"type\": \"custom\",\n" +
- " \"created\": \"" + date + "T00:00:00.000Z\",\n" +
- " \"lastUsed\": \"2100-01-01T00:00:00.000Z\",\n" +
- " \"lastVersionId\": \"BBOR-@VERSION@\"\n" +
- " },");
-
- Files.write(profilesJson.toPath(), contents.getBytes());
- }
- } catch (Throwable t) {
- t.printStackTrace();
- }
-
- // Copy rift jar to libraries
- try {
- String source = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
- if (source.startsWith("/") && osName.contains("win")) {
- source = source.substring(1);
- }
- File riftJar = new File(minecraftFolder, "libraries/com/irtimaled/bbor/@VERSION@/bbor-@VERSION@.jar");
- riftJar.getParentFile().mkdirs();
- Files.copy(Paths.get(source), riftJar.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } catch (Throwable t) {
- t.printStackTrace();
- }
-
- JOptionPane.showMessageDialog(null,
- "Bounding Box Outline Reloaded @VERSION@ has been successfully installed!\n" +
- "\n" +
- "Re-open the Minecraft Launcher to see it in the dropdown.",
- "Bounding Box Outline Reloaded Installer", JOptionPane.INFORMATION_MESSAGE);
- } catch (Throwable t) {
- StringWriter w = new StringWriter();
- t.printStackTrace(new PrintWriter(w));
- JOptionPane.showMessageDialog(null,
- "An error occured while installing Bounding Box Outline Reloaded, please report this to the issue\n" +
- "tracker (https://github.com/irtimaled/BoundingBoxOutlineReloaded/issues):\n" +
- "\n" +
- w.toString().replace("\t", " "), "Bounding Box Outline Reloaded Installer", JOptionPane.ERROR_MESSAGE);
- }
- }
-
- private static File getMinecraftFolder(String osName) {
- File minecraftFolder;
- if (osName.contains("win")) {
- minecraftFolder = new File(System.getenv("APPDATA") + "/.minecraft");
- } else if (osName.contains("mac")) {
- minecraftFolder = new File(System.getProperty("user.home") + "/Library/Application Support/minecraft");
- } else {
- minecraftFolder = new File(System.getProperty("user.home") + "/.minecraft");
- }
- return minecraftFolder;
- }
-
- private static String getOsName() {
- return System.getProperty("os.name").toLowerCase(Locale.ROOT);
- }
-}
+++ /dev/null
-package com.irtimaled.bbor.mixin.entity.player;
-
-import com.irtimaled.bbor.common.EventBus;
-import com.irtimaled.bbor.common.events.PlayerChangedDimension;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.player.EntityPlayerMP;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-
-@Mixin(EntityPlayerMP.class)
-public class MixinEntityPlayerMP {
- @Inject(method = "changeDimension", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/management/PlayerList;changePlayerDimension(Lnet/minecraft/entity/player/EntityPlayerMP;I)V"))
- private void changeDimension(int dimensionId, CallbackInfoReturnable<Entity> cir) {
- EventBus.publish(new PlayerChangedDimension((EntityPlayerMP) (Object) this));
- }
-}
public class MixinPlayerList {
@Inject(method = "playerLoggedIn", at = @At("RETURN"))
private void playerLoggedIn(EntityPlayerMP player, CallbackInfo ci) {
- if (player.world.isRemote) {
- EventBus.publish(new PlayerLoggedIn(player));
- }
+ EventBus.publish(new PlayerLoggedIn(player));
}
@Inject(method = "playerLoggedOut", at = @At("HEAD"))
private void playerLoggedOut(EntityPlayerMP player, CallbackInfo ci) {
- if (player.world.isRemote) {
- EventBus.publish(new PlayerLoggedOut(player));
- }
+ EventBus.publish(new PlayerLoggedOut(player));
}
}
--- /dev/null
+package com.irtimaled.bbor.server;
+
+import net.minecraft.launchwrapper.Launch;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.channels.Channels;
+import java.util.*;
+import java.util.function.Consumer;
+
+public class ServerRunner {
+ private static final Map<String, String> VANILLA_SERVER_JARS = new HashMap<>();
+
+ private static final String[] LIBRARIES = {
+ "https://github.com/irtimaled/Mixin/releases/download/org/spongepowered/mixin/0.7.11-SNAPSHOT/mixin-0.7.11-SNAPSHOT.jar",
+ "https://repo1.maven.org/maven2/org/ow2/asm/asm/6.2/asm-6.2.jar",
+ "https://repo1.maven.org/maven2/org/ow2/asm/asm-commons/6.2/asm-commons-6.2.jar",
+ "https://repo1.maven.org/maven2/org/ow2/asm/asm-tree/6.2/asm-tree-6.2.jar",
+ "https://libraries.minecraft.net/net/minecraft/launchwrapper/1.12/launchwrapper-1.12.jar"
+ };
+
+ //private static final MethodHandle addURLHandle;
+
+ private static final ThrowableConsumer<URL> addURL;
+
+ static {
+ VANILLA_SERVER_JARS.put("1.13.2", "https://launcher.mojang.com/v1/objects/3737db93722a9e39eeada7c27e7aca28b144ffa7/server.jar");
+ VANILLA_SERVER_JARS.put("1.13.1", "https://launcher.mojang.com/v1/objects/fe123682e9cb30031eae351764f653500b7396c9/server.jar");
+ VANILLA_SERVER_JARS.put("1.13", "https://launcher.mojang.com/v1/objects/d0caafb8438ebd206f99930cfaecfa6c9a13dca0/server.jar");
+
+ try {
+ Method method = URLClassLoader.class
+ .getDeclaredMethod("addURL", URL.class);
+ method.setAccessible(true);
+ // addURLHandle = MethodHandles.lookup().unreflect(method);
+ addURL = url -> method.invoke(ClassLoader.getSystemClassLoader(), url);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static void addURLToClasspath(File file) throws MalformedURLException {
+ //try {
+ addURL.accept(file.toURI().toURL());
+ //addURLHandle.invoke(ClassLoader.getSystemClassLoader(), file.toURI().toURL());
+ //} catch (Throwable t) {
+ // throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
+ // }
+ }
+
+ public static void run(String version, List<String> args) throws IOException {
+ String serverJarUrl = VANILLA_SERVER_JARS.get(version);
+
+ addURLToClasspath(getOrDownload(new File("."), serverJarUrl));
+ for (String url : LIBRARIES) {
+ addURLToClasspath(getOrDownload(new File("libs"), url));
+ }
+
+ args = new ArrayList<>(args);
+ args.add("--tweakClass");
+ args.add("com.irtimaled.bbor.launch.ServerTweaker");
+
+ System.out.println("Launching server...");
+ Launch.main(args.toArray(new String[0]));
+ }
+
+ private static File getOrDownload(File directory, String url) throws IOException {
+ String fileName = url.substring(url.lastIndexOf('/') + 1);
+ File target = new File(directory, fileName);
+ if (target.isFile()) {
+ return target;
+ }
+ target.getParentFile().mkdirs();
+
+ System.out.println("Downloading library: " + url);
+ new FileOutputStream(target).getChannel()
+ .transferFrom(Channels.newChannel(new URL(url).openStream()), 0, Long.MAX_VALUE);
+
+ return target;
+ }
+
+}
--- /dev/null
+package com.irtimaled.bbor.server;
+
+import java.util.function.Consumer;
+
+public interface ThrowableConsumer<T> extends Consumer<T> {
+ @Override
+ default void accept(final T elem) {
+ try {
+ acceptThrows(elem);
+ } catch (final Throwable t) {
+ throw t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t);
+ }
+ }
+
+ void acceptThrows(T elem) throws Throwable;
+}
\ No newline at end of file
"mixins": [
"world.chunk.MixinChunk",
"network.play.client.MixinCPacketCustomPayload",
- "network.play.server.MixinSPacketCustomPayload",
- "entity.player.MixinEntityPlayerMP",
"village.MixinVillage",
"server.management.MixinPlayerList",
"server.management.MixinPlayerInteractionManager",
"client.MixinMinecraft",
"client.gui.MixinGuiOptions",
"client.renderer.MixinEntityRenderer",
+ "network.play.server.MixinSPacketCustomPayload",
"client.multiplayer.MixinWorldClient",
"client.network.MixinNetHandlerLoginClient",
"client.settings.MixinKeyBinding",