Skip to content

Commit df99ffd

Browse files
authored
[Savestates] Add temporary savestates to /record and /play (#273)
- Add `nosave` argument to /play and /record - Fixed current savestate not being renamed
2 parents 87016dd + c0290f6 commit df99ffd

File tree

7 files changed

+138
-28
lines changed

7 files changed

+138
-28
lines changed

src/main/java/com/minecrafttas/tasmod/commands/CommandClearInputs.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.minecrafttas.tasmod.commands;
22

33
import com.minecrafttas.tasmod.TASmod;
4-
import com.minecrafttas.tasmod.networking.TASmodBufferBuilder;
5-
import com.minecrafttas.tasmod.registries.TASmodPackets;
64

75
import net.minecraft.command.CommandBase;
86
import net.minecraft.command.CommandException;
@@ -25,11 +23,7 @@ public String getUsage(ICommandSender sender) {
2523
@Override
2624
public void execute(MinecraftServer server, ICommandSender sender, String[] args) throws CommandException {
2725
if (sender instanceof EntityPlayer) {
28-
try {
29-
TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.PLAYBACK_CLEAR_INPUTS));
30-
} catch (Exception e) {
31-
e.printStackTrace();
32-
}
26+
TASmod.playbackControllerServer.clearInputs();
3327
}
3428
}
3529

src/main/java/com/minecrafttas/tasmod/commands/CommandPlay.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public String getName() {
2323

2424
@Override
2525
public String getUsage(ICommandSender sender) {
26-
return "/play";
26+
return "/play [nosave]";
2727
}
2828

2929
@Override
@@ -41,16 +41,23 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args
4141
if (!(sender instanceof EntityPlayer)) {
4242
return;
4343
}
44-
if (args.length < 1) {
45-
TASmod.playbackControllerServer.togglePlayback();
46-
} else if (args.length > 1) {
44+
if (args.length <= 1) {
45+
boolean loadTempSavestate = true;
46+
if (args.length == 1 && "nosave".equals(args[0])) {
47+
loadTempSavestate = false;
48+
}
49+
TASmod.playbackControllerServer.togglePlayback(loadTempSavestate);
50+
} else if (args.length > 2) {
4751
sender.sendMessage(new TextComponentString(TextFormatting.RED + "Too many arguments. " + getUsage(sender)));
4852
}
4953

5054
}
5155

5256
@Override
5357
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) {
58+
if (args.length == 1) {
59+
return getListOfStringsMatchingLastWord(args, "nosave");
60+
}
5461
return super.getTabCompletions(server, sender, args, targetPos);
5562
}
5663
}

src/main/java/com/minecrafttas/tasmod/commands/CommandRecord.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import net.minecraft.command.ICommandSender;
1111
import net.minecraft.entity.player.EntityPlayer;
1212
import net.minecraft.server.MinecraftServer;
13+
import net.minecraft.util.math.BlockPos;
1314
import net.minecraft.util.text.TextComponentString;
1415
import net.minecraft.util.text.TextFormatting;
1516

@@ -22,7 +23,7 @@ public String getName() {
2223

2324
@Override
2425
public String getUsage(ICommandSender sender) {
25-
return "/record";
26+
return "/record [nosave]";
2627
}
2728

2829
@Override
@@ -40,14 +41,23 @@ public void execute(MinecraftServer server, ICommandSender sender, String[] args
4041
if (!(sender instanceof EntityPlayer)) {
4142
return;
4243
}
43-
if (args.length < 1) {
44-
TASmod.playbackControllerServer.toggleRecording();
45-
// TASmod.tickSchedulerServer.add(() ->{
46-
// TASmod.ktrngHandler.broadcastStartSeed();
47-
// });
44+
if (args.length <= 1) {
45+
boolean saveTempSavestate = true;
46+
if (args.length == 1 && "nosave".equals(args[0])) {
47+
saveTempSavestate = false;
48+
}
49+
TASmod.playbackControllerServer.toggleRecording(saveTempSavestate);
4850
} else if (args.length > 1) {
4951
sender.sendMessage(new TextComponentString(TextFormatting.RED + "Too many arguments. " + getUsage(sender)));
5052
}
5153

5254
}
55+
56+
@Override
57+
public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) {
58+
if (args.length == 1) {
59+
return getListOfStringsMatchingLastWord(args, "nosave");
60+
}
61+
return super.getTabCompletions(server, sender, args, targetPos);
62+
}
5363
}

src/main/java/com/minecrafttas/tasmod/playback/PlaybackControllerServer.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public class PlaybackControllerServer implements ServerPacketHandler {
3333

3434
private TASstate state;
3535

36+
private boolean createState = true;
37+
3638
@Override
3739
public PacketID[] getAcceptedPacketIDs() {
3840
//@formatter:off
@@ -62,7 +64,7 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws
6264
break;
6365

6466
case PLAYBACK_CLEAR_INPUTS:
65-
TASmod.server.sendToAll(new TASmodBufferBuilder(PLAYBACK_CLEAR_INPUTS));
67+
clearInputs();
6668
break;
6769
case PLAYBACK_FULLPLAY:
6870
case PLAYBACK_FULLRECORD:
@@ -98,14 +100,42 @@ public void setServerState(TASstate stateIn) {
98100
}
99101
}
100102

101-
public void toggleRecording() {
103+
public void toggleRecording(boolean saveSavestate) {
104+
if (state == TASstate.NONE && createState && saveSavestate) {
105+
createState = false;
106+
TASmod.savestateHandlerServer.saveStateTemp((paths) -> {
107+
try {
108+
TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_CLEAR_SCREEN));
109+
} catch (Exception e) {
110+
TASmod.LOGGER.catching(e);
111+
}
112+
});
113+
}
102114
setState(state == TASstate.RECORDING ? TASstate.NONE : TASstate.RECORDING);
103115
}
104116

105-
public void togglePlayback() {
117+
public void togglePlayback(boolean loadSavestate) {
118+
if (state == TASstate.NONE && loadSavestate) {
119+
TASmod.savestateHandlerServer.loadStateTemp((paths) -> {
120+
try {
121+
TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_CLEAR_SCREEN));
122+
} catch (Exception e) {
123+
TASmod.LOGGER.catching(e);
124+
}
125+
});
126+
}
106127
setState(state == TASstate.PLAYBACK ? TASstate.NONE : TASstate.PLAYBACK);
107128
}
108129

130+
public void clearInputs() {
131+
createState = true;
132+
try {
133+
TASmod.server.sendToAll(new TASmodBufferBuilder(PLAYBACK_CLEAR_INPUTS));
134+
} catch (Exception e) {
135+
e.printStackTrace();
136+
}
137+
}
138+
109139
public TASstate getState() {
110140
return state;
111141
}

src/main/java/com/minecrafttas/tasmod/savestates/SavestateHandlerServer.java

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import java.util.List;
1212
import java.util.stream.Collectors;
1313

14+
import org.apache.commons.lang3.ArrayUtils;
1415
import org.apache.logging.log4j.Logger;
1516

1617
import com.minecrafttas.mctcommon.events.EventListenerRegistry;
@@ -135,23 +136,40 @@ public void saveState(int index, String name, SavestateCallback cb, SavestateFla
135136

136137
logger.trace("Create new savestate index via indexer");
137138
SavestatePaths paths = indexer.createSavestate(index, name, !SavestateFlags.BLOCK_CHANGE_INDEX.isBlocked(flags));
139+
140+
if (paths.getSavestate().index == 0) {
141+
if (!ArrayUtils.contains(flags, SavestateFlags.BLOCK_CLIENT_SAVESTATE))
142+
flags = ArrayUtils.add(flags, SavestateFlags.BLOCK_CLIENT_SAVESTATE);
143+
}
144+
145+
savestateInner(paths, cb, flags);
146+
}
147+
148+
public void saveStateTemp(SavestateCallback cb) {
149+
SavestatePaths paths = indexer.createTempSavestate();
150+
SavestateFlags[] flags = new SavestateFlags[] { SavestateFlags.BLOCK_CLIENT_SAVESTATE, SavestateFlags.BLOCK_PAUSE_TICKRATE };
151+
savestateInner(paths, cb, flags);
152+
paths.getSavestate().save();
153+
}
154+
155+
private void savestateInner(SavestatePaths paths, SavestateCallback cb, SavestateFlags... flags) {
138156
Path sourceFolder = paths.getSourceFolder();
139157
Path targetFolder = paths.getTargetFolder();
140-
int indexToSave = paths.getSavestate().index;
158+
Integer indexToSave = paths.getSavestate().index;
141159
logger.debug("Source: {}, Target: {}", sourceFolder, targetFolder);
142-
143160
EventListenerRegistry.fireEvent(EventSavestate.EventServerSavestate.class, server, paths);
144161

145162
if (Files.exists(targetFolder)) {
146-
logger.warn(LoggerMarkers.Savestate, "WARNING! Overwriting the savestate with the index {}", indexToSave);
163+
if (indexToSave != null)
164+
logger.warn(LoggerMarkers.Savestate, "WARNING! Overwriting the savestate with the index {}", indexToSave);
147165
deleteFolder(targetFolder);
148166
}
149167

150168
/*
151169
* Prevents creating an InputSavestate when saving at index 0 (Index 0 is the
152170
* savestate when starting a recording)
153171
*/
154-
if (index != 0) {
172+
if (!SavestateFlags.BLOCK_CLIENT_SAVESTATE.isBlocked(flags)) {
155173
/*
156174
* Send the name of the world to all players. This will make a savestate of the
157175
* recording on the client with that name
@@ -235,10 +253,27 @@ public void loadState(int index, String name, SavestateCallback cb, SavestateFla
235253
SavestatePaths paths = indexer.loadSavestate(index, !SavestateFlags.BLOCK_CHANGE_INDEX.isBlocked(flags));
236254
logger.debug(LoggerMarkers.Savestate, "Source: {}, Target: {}", paths.getSourceFolder(), paths.getTargetFolder());
237255

256+
if (paths.getSavestate().index == 0) {
257+
if (!ArrayUtils.contains(flags, SavestateFlags.BLOCK_CLIENT_SAVESTATE))
258+
flags = ArrayUtils.add(flags, SavestateFlags.BLOCK_CLIENT_SAVESTATE);
259+
}
260+
261+
loadStateInner(paths, cb, flags);
262+
}
263+
264+
public void loadStateTemp(SavestateCallback cb) {
265+
SavestatePaths paths = indexer.loadTempSavestate();
266+
if (paths == null)
267+
return;
268+
SavestateFlags[] flags = new SavestateFlags[] { SavestateFlags.BLOCK_CLIENT_SAVESTATE, SavestateFlags.BLOCK_PAUSE_TICKRATE };
269+
loadStateInner(paths, cb, flags);
270+
paths.getSavestate().save();
271+
}
272+
273+
private void loadStateInner(SavestatePaths paths, SavestateCallback cb, SavestateFlags... flags) {
238274
String worldname = server.getFolderName();
239275
Path sourcefolder = paths.getSourceFolder();
240276
Path targetfolder = paths.getTargetFolder();
241-
int indexToLoad = paths.getSavestate().index;
242277

243278
EventListenerRegistry.fireEvent(EventSavestate.EventServerLoadstate.class, server, paths);
244279

@@ -247,7 +282,7 @@ public void loadState(int index, String name, SavestateCallback cb, SavestateFla
247282
* savestate when starting a recording. Not doing this will load an empty
248283
* InputSavestate)
249284
*/
250-
if (indexToLoad != 0) {
285+
if (!SavestateFlags.BLOCK_CLIENT_SAVESTATE.isBlocked(flags)) {
251286
try {
252287
// loadstate inputs client
253288
TASmod.server.sendToAll(new TASmodBufferBuilder(TASmodPackets.SAVESTATE_LOAD).writeString(paths.getSavestate().folder.toString()));
@@ -576,6 +611,10 @@ public static enum SavestateFlags {
576611
* Stops updating the current index when savestating/loadstating
577612
*/
578613
BLOCK_CHANGE_INDEX,
614+
/**
615+
* Stops the creation/loading of a client savestate
616+
*/
617+
BLOCK_CLIENT_SAVESTATE,
579618
/**
580619
* Stops setting the tickrate to 0 after a savestate/loadstate
581620
*/
@@ -614,6 +653,10 @@ public void rename(int index, String name) throws SavestateException {
614653
rename(index, name, null);
615654
}
616655

656+
public void renameCurrent(String name) throws SavestateException {
657+
indexer.renameCurrent(name);
658+
}
659+
617660
public void rename(int index, String name, SavestateCallback cb) throws SavestateException {
618661
SavestatePaths paths = indexer.renameSavestate(index, name);
619662
if (cb != null) {

src/main/java/com/minecrafttas/tasmod/savestates/SavestateIndexer.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.stream.Stream;
2121

2222
import org.apache.logging.log4j.Logger;
23+
import org.jetbrains.annotations.Nullable;
2324

2425
import com.minecrafttas.mctcommon.file.AbstractDataFile;
2526
import com.minecrafttas.tasmod.TASmod;
@@ -239,6 +240,11 @@ public SavestatePaths renameSavestate(int index, String name) throws SavestateEx
239240
return SavestatePaths.of(savestateToRename, null, null);
240241
}
241242

243+
public void renameCurrent(String name) {
244+
currentSavestate.name = name;
245+
currentSavestate.save();
246+
}
247+
242248
/**
243249
* Deletes a savestate
244250
* @param index The index to delete
@@ -379,7 +385,8 @@ public void run() {
379385
Throwable error = new SavestateException("Savestate.json data file not found in " + savestateBaseDirectory.relativize(savestateDat));
380386
savestate = new FailedSavestate(path, backupIndex, null, null, error);
381387
}
382-
savestateList.put(savestate.getIndex(), savestate.clone());
388+
if (savestate.index != null) // Temporary savestates have no index and are not listed in the savestatelist
389+
savestateList.put(savestate.getIndex(), savestate.clone());
383390
});
384391
sortSavestateList();
385392
try {
@@ -473,14 +480,17 @@ public Savestate getCurrentSavestate() {
473480
*/
474481
public class Savestate extends AbstractDataFile {
475482

483+
@Nullable
476484
protected Integer index;
485+
@Nullable
477486
protected String name;
487+
@Nullable
478488
protected Date date;
479489
protected Path folder;
480490
protected Logger logger = TASmod.LOGGER;
481491

482492
private Savestate(Path datFile, Path folder) {
483-
this(datFile, -1, null, null, folder);
493+
this(datFile, null, null, null, folder);
484494
}
485495

486496
private Savestate(Path file, Integer index, String name, Date date, Path folder) {
@@ -742,4 +752,19 @@ public int getNextIndex(int index) {
742752
}
743753
return index;
744754
}
755+
756+
public SavestatePaths createTempSavestate() {
757+
Path sourceDirectory = savesDir.resolve(worldname);
758+
Path targetDirectory = currentSavestateDir.resolve(worldname + "-SavestateTemp");
759+
Savestate tempSavestate = new Savestate(targetDirectory.resolve(savestateFilePath), null, "Temporary Savestate!", new Date(), targetDirectory);
760+
return SavestatePaths.of(tempSavestate, sourceDirectory, targetDirectory);
761+
}
762+
763+
public SavestatePaths loadTempSavestate() {
764+
Path sourceDirectory = currentSavestateDir.resolve(worldname + "-SavestateTemp");
765+
if (!Files.exists(sourceDirectory))
766+
return null;
767+
Path targetDirectory = savesDir.resolve(worldname);
768+
return SavestatePaths.of(currentSavestate.clone(), sourceDirectory, targetDirectory);
769+
}
745770
}

src/main/java/com/minecrafttas/tasmod/savestates/handlers/SavestateGuiHandlerServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public void onServerPacket(PacketID id, ByteBuffer buf, String username) throws
3434
String name = TASmodBufferBuilder.readString(buf);
3535
TASmod.gameLoopSchedulerServer.add(() -> {
3636
TASmod.savestateHandlerServer.rename(index, name);
37+
TASmod.savestateHandlerServer.renameCurrent(name);
3738
});
3839
TASmod.server.sendToAll(new TASmodBufferBuilder(SAVESTATE_CLEAR_SCREEN));
3940

0 commit comments

Comments
 (0)