Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/com/minecrafttas/tasmod/TASmod.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.minecrafttas.tasmod.playback.metadata.builtin.StartpositionMetadataExtension;
import com.minecrafttas.tasmod.registries.TASmodPackets;
import com.minecrafttas.tasmod.savestates.SavestateHandlerServer;
import com.minecrafttas.tasmod.savestates.handlers.SavestateResourcePackHandler;
import com.minecrafttas.tasmod.savestates.storage.builtin.SavestateMotionStorage;
import com.minecrafttas.tasmod.tickratechanger.TickrateChangerServer;
import com.minecrafttas.tasmod.ticksync.TickSyncServer;
Expand Down Expand Up @@ -119,6 +120,9 @@ public void onInitialize() {
SavestateMotionStorage motionStorage = new SavestateMotionStorage();
PacketHandlerRegistry.register(motionStorage);
EventListenerRegistry.register(motionStorage);
SavestateResourcePackHandler resourcepackHandler = new SavestateResourcePackHandler();
PacketHandlerRegistry.register(resourcepackHandler);
EventListenerRegistry.register(resourcepackHandler);
PacketHandlerRegistry.register(playUntil);
EventListenerRegistry.register(playUntil);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,41 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args
} else if (args.length >= 1) {
if ("save".equals(args[0])) {
if (args.length == 1) {
saveLatest();
TASmod.gameLoopSchedulerServer.add(() -> {
try {
saveLatest();
} catch (CommandException e) {
e.printStackTrace();
}
});
} else if (args.length == 2) {
saveWithIndex(args);
TASmod.gameLoopSchedulerServer.add(() -> {
try {
saveWithIndex(args);
} catch (CommandException e) {
e.printStackTrace();
}
});
} else {
throw new CommandException("Too many arguments!", new Object[] {});
}
} else if ("load".equals(args[0])) {
if (args.length == 1) {
loadLatest();
TASmod.gameLoopSchedulerServer.add(() -> {
try {
loadLatest();
} catch (CommandException e) {
e.printStackTrace();
}
});
} else if (args.length == 2) {
loadLatest(args);
TASmod.gameLoopSchedulerServer.add(() -> {
try {
loadLatest(args);
} catch (CommandException e) {
e.printStackTrace();
}
});
} else {
throw new CommandException("Too many arguments!", new Object[] {});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.minecrafttas.tasmod.mixin.savestates;

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.CallbackInfo;

import com.minecrafttas.tasmod.savestates.handlers.SavestateResourcePackHandler;

import net.minecraft.client.Minecraft;

@Mixin(Minecraft.class)
public class MixinMinecraft {

@Inject(method = "refreshResources", at = @At(value = "RETURN"))
public void inject_refreshResources(CallbackInfo ci) {
if (SavestateResourcePackHandler.clientRPLatch != null && SavestateResourcePackHandler.clientRPLatch.getCount() > 0) {
SavestateResourcePackHandler.clientRPLatch.countDown();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ public enum TASmodPackets implements PacketID {
Minecraft mc = Minecraft.getMinecraft();
((ScoreboardDuck) mc.world.getScoreboard()).clearScoreboard();
}),
/**
* <p>Clears the resourcepack on the client side
* <p>SIDE: Client<br>
* ARGS: none
*/
SAVESTATE_CLEAR_RESOURCEPACK,
/**
* <p>Notifies the client to clear all inputs from the input buffer in {@link PlaybackControllerClient}
* <p>SIDE: Both<br>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import com.minecrafttas.tasmod.savestates.files.SavestateDataFile.DataValues;
import com.minecrafttas.tasmod.savestates.files.SavestateTrackerFile;
import com.minecrafttas.tasmod.savestates.handlers.SavestatePlayerHandler;
import com.minecrafttas.tasmod.savestates.handlers.SavestateResourcePackHandler;
import com.minecrafttas.tasmod.savestates.handlers.SavestateWorldHandler;
import com.minecrafttas.tasmod.util.LoggerMarkers;
import com.minecrafttas.tasmod.util.Scheduler.Task;
Expand Down Expand Up @@ -386,6 +387,9 @@ public void loadState(int savestateIndex, boolean tickrate0, boolean changeIndex
// Reenable level saving
worldHandler.enableLevelSaving();

// Refresh server resourcepacks on the client
SavestateResourcePackHandler.refreshServerResourcepack(server);

// Incrementing info file
SavestateTrackerFile tracker = new SavestateTrackerFile(savestateDirectory.resolve(worldname + "-info.txt"));
tracker.increaseLoadstateCount();
Expand Down Expand Up @@ -753,9 +757,9 @@ private int legacyIndexFile(Path savestateDat) {
public PacketID[] getAcceptedPacketIDs() {
return new TASmodPackets[] {
//@formatter:off
TASmodPackets.SAVESTATE_SAVE,
TASmodPackets.SAVESTATE_LOAD,
TASmodPackets.SAVESTATE_SCREEN,
TASmodPackets.SAVESTATE_SAVE,
TASmodPackets.SAVESTATE_LOAD,
TASmodPackets.SAVESTATE_SCREEN,
TASmodPackets.SAVESTATE_UNLOAD_CHUNKS
//@formatter:on
};
Expand All @@ -770,7 +774,7 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws

switch (packet) {
case SAVESTATE_SAVE:
Integer index = TASmodBufferBuilder.readInt(buf);
int index = TASmodBufferBuilder.readInt(buf);

Task savestateTask = () -> {
try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.minecrafttas.tasmod.savestates.gui;

import com.minecrafttas.tasmod.util.MessageUtils;

import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.resources.I18n;

/**
* Screen for warning the player that a "reources.zip" is present in the world folder,<br>
* which significantly slows down savestates
*/
public class GuiResourcepackWarn extends GuiScreen {

/**
* Screen for warning the player that a "reources.zip" is present in the world folder,<br>
* which significantly slows down savestates
*/
public GuiResourcepackWarn() {
this.mc = Minecraft.getMinecraft();
}

@Override
public void drawScreen(int mouseX, int mouseY, float partialTicks) {
this.drawDefaultBackground();

ScaledResolution scaled = new ScaledResolution(Minecraft.getMinecraft());
int width = scaled.getScaledWidth();
int height = scaled.getScaledHeight();

MessageUtils.splitNewline(I18n.format("gui.tasmod.savestate.resourcepack"), 15, (line, y) -> {
drawCenteredString(fontRenderer, line, width / 2, height / 4 + 40 + y, 0xFF5555);
});

super.drawScreen(mouseX, mouseY, partialTicks);
}

@Override
public boolean doesGuiPauseGame() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.minecrafttas.tasmod.savestates.handlers;

import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import com.minecrafttas.mctcommon.networking.Client.Side;
import com.minecrafttas.mctcommon.networking.exception.PacketNotImplementedException;
import com.minecrafttas.mctcommon.networking.exception.WrongSideException;
import com.minecrafttas.mctcommon.networking.interfaces.ClientPacketHandler;
import com.minecrafttas.mctcommon.networking.interfaces.PacketID;
import com.minecrafttas.mctcommon.networking.interfaces.ServerPacketHandler;
import com.minecrafttas.tasmod.TASmod;
import com.minecrafttas.tasmod.TASmodClient;
import com.minecrafttas.tasmod.events.EventSavestate;
import com.minecrafttas.tasmod.networking.TASmodBufferBuilder;
import com.minecrafttas.tasmod.registries.TASmodPackets;
import com.minecrafttas.tasmod.savestates.exceptions.SavestateException;
import com.minecrafttas.tasmod.savestates.gui.GuiResourcepackWarn;
import com.minecrafttas.tasmod.util.LoggerMarkers;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.server.MinecraftServer;

/**
* Handles reloading server resourcepacks when loadstating.
*
* @author Scribble
*/
public class SavestateResourcePackHandler implements EventSavestate.EventServerLoadstate, ServerPacketHandler, ClientPacketHandler {

/**
* The server future for waiting until the client is done unloading the RP
*/
private CompletableFuture<String> serverRPFuture;

/**
* The latch for waiting until the client RP is unloaded
*/
public static CountDownLatch clientRPLatch;

@Override
public void onServerLoadstate(MinecraftServer server, int index, Path target, Path current) {
if (server.getResourcePackUrl().isEmpty() || server.isDedicatedServer())
return;

String serverOwnerName = server.getServerOwner();

try {
TASmod.server.sendTo(serverOwnerName, new TASmodBufferBuilder(TASmodPackets.SAVESTATE_CLEAR_RESOURCEPACK));
} catch (Exception e) {
TASmod.LOGGER.catching(e);
}
serverRPFuture = new CompletableFuture<>();

String playername = null;
try {
playername = serverRPFuture.get(2L, TimeUnit.MINUTES);
} catch (TimeoutException e) {
throw new SavestateException(e, "Clearing resourcepacks %s timed out!", serverOwnerName);
} catch (ExecutionException | InterruptedException e) {
throw new SavestateException(e, "Clearing resourcepacks %s", serverOwnerName);
}

server.setResourcePack("", "");
TASmod.LOGGER.debug(LoggerMarkers.Savestate, "Cleared resourcepack for player {}", playername);
}

@Override
public PacketID[] getAcceptedPacketIDs() {
return new TASmodPackets[] { TASmodPackets.SAVESTATE_CLEAR_RESOURCEPACK };
}

@Environment(EnvType.CLIENT)
@Override
public void onClientPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception {
TASmodPackets packetId = (TASmodPackets) id;

Minecraft mc = Minecraft.getMinecraft();
switch (packetId) {
case SAVESTATE_CLEAR_RESOURCEPACK:

TASmod.LOGGER.debug(LoggerMarkers.Savestate, "Clearing server resource pack");

mc.displayGuiScreen(new GuiResourcepackWarn());

/**
* Using a countdown latch here, which is counted down in
* savestates.MixinMinecraft.
*
* Clearing the resourcepack is scheduled multiple times
* so for simplicity, I use a latch here.
*/
clientRPLatch = new CountDownLatch(1);
mc.getResourcePackRepository().clearResourcePack();

try {
clientRPLatch.await(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}

/**
* At this point, "clearResourcePack" did remove the server RP
* however, the file association with the "resources.zip" in the
* save folder is still there, which causes loadstating to fail,
* as the system still thinks that the RP is still "in use".
*
* We have to run the garbage collector to remove it.
*/
System.gc();

/**
* Notify the server that savestates have been cleared and that savestating can continue
*/
try {
TASmodClient.client.send(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_CLEAR_RESOURCEPACK));
} catch (Exception e) {
TASmod.LOGGER.catching(e);
}
break;

default:
throw new WrongSideException(packetId, Side.CLIENT);
}
}

@Override
public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws PacketNotImplementedException, WrongSideException, Exception {
TASmodPackets packetId = (TASmodPackets) id;

switch (packetId) {
case SAVESTATE_CLEAR_RESOURCEPACK:
serverRPFuture.complete(username);
break;

default:
throw new WrongSideException(packetId, Side.SERVER);
}
}

/**
* Notifies all clients that a new server resourcepack should be downloaded if available
*
* @param server The Minecraft server
*/
public static void refreshServerResourcepack(MinecraftServer server) {
TASmod.LOGGER.debug(LoggerMarkers.Savestate, "Refreshing resourcepack");
List<EntityPlayerMP> players = server.getPlayerList().getPlayers();
players.forEach((player) -> {
player.loadResourcePack(server.getResourcePackUrl(), server.getResourcePackHash());
});
}
}
23 changes: 23 additions & 0 deletions src/main/java/com/minecrafttas/tasmod/util/MessageUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.minecrafttas.tasmod.util;

public class MessageUtils {

public static void splitNewline(String phrase, GuiInterface method) {
splitNewline(phrase, 10, method);
}

public static void splitNewline(String phrase, int distance, GuiInterface method) {
int y = 0;
String[] lines = phrase.split("\r?\n");
for (String line : lines) {
method.draw(line, y);
y += distance;
}
}

@FunctionalInterface
public interface GuiInterface {

public void draw(String line, int y);
}
}
5 changes: 3 additions & 2 deletions src/main/resources/assets/tasmod/lang/en_us.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"tickratechanger.tasmod.command.show": "Current tickrate: %s",
"tickratechanger.tasmod.command.usage": "/tickrate <ticks per second>"
"tickratechanger.tasmod.command.show":"Current tickrate: %s",
"tickratechanger.tasmod.command.usage":"/tickrate <ticks per second>",
"gui.tasmod.savestate.resourcepack":"You have a resource pack (resources.zip) in your world folder.\nLoadstating will take severely longer!"
}
1 change: 1 addition & 0 deletions src/main/resources/tasmod.mixin.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
// Savestates
"savestates.MixinChunkProviderClient",
"savestates.MixinWorldClient",
"savestates.MixinMinecraft",

// Interpolation
"MixinFrustum",
Expand Down