Skip to content

Commit ce5b56a

Browse files
committed
Add rewind types and /loop settings setRewindType
- Added new command that teleports the player on each iteration - Also removed pdf (again)
1 parent ec7bc78 commit ce5b56a

16 files changed

Lines changed: 200 additions & 52 deletions

File tree

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@ Simply use commands to configure the loop.
1616

1717
**/loop**
1818
- `start` - Start the loop.
19+
- `skip` - Skip the loop and advance to the next iteration.
1920
- `stop` - Stop the loop.
2021
- `reset` - Reset the loop and go back to the first recording. **This doesn't delete the recordings as of yet but it's being worked on.**
2122
- `status` - Shows the status of the loop in chat.
2223
- **settings**
2324
- `maxLoops [0]` - Sets the maximum amount of loops. 0 is infinite.
2425
- `setLength [6000]` - Set the duration / length of the loop in ticks (6000 ticks is 5 mins).
2526
- `setTimeOfDay [13000]` - Sets the time of day to loop at (same as minecraft so 13000 is night).
26-
- `loopType [TICK]` - Sets the type of loop.
27+
- `setLoopType [TICK]` - Sets the type of loop.
2728
- `modifyPlayer [target_player] [nickname] [skin]` - Changes a looped player's nickname and skin.
29+
- `setRewindType [NONE]` - Sets the rewind type.
2830
- **toggles**
2931
- `trackTimeOfDay [true]` - Toggles tracking the time of day during loops.
3032
- `trackItems [false]` - Toggles tracking items during loops.
@@ -33,12 +35,17 @@ Simply use commands to configure the loop.
3335
- `trackChat [false]` - Toggles tracking chat during loops.
3436
- `hurtLoopedPlayers [false]` - Toggles being able to hit looped players.
3537

36-
# LoopType Options
38+
# Loop Types
3739
- `TICK` (Loops every `setLength` ticks)
3840
- `TIME_OF_DAY` (Loops when the time reaches `setTimeOfDay`)
3941
- `SLEEP` (Loops when you sleep)
4042
- `DEATH` (Loops when you die)
4143

44+
# Rewind Types
45+
- `NONE` (Doesn't rewind)
46+
- `START_POSITION` (Rewinds players to their position at the start of the loop)
47+
- `JOIN_POSITION` (Rewinds players to their position when they joined)
48+
4249
# Known Issues
4350
- Dying stops the player from looping
4451
- You can see spectators' held item and worn armour

build.gradle

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ subprojects {
6060
inputs.properties replaceProperties
6161
}
6262

63-
jar {
64-
exclude "assets/time-loop/icon.psd"
65-
}
66-
6763
if (project.name == "fabric") {
6864
tasks.named("jar") {
6965
finalizedBy rootProject.tasks.named("openBuildDirFabric")

common/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@ loom {
2424
mixin.useLegacyMixinAp = false
2525
officialMojangMappings()
2626
}
27+
28+
jar {
29+
exclude "icon.psd"
30+
}

common/src/main/java/com/vltno/timeloop/LoopSceneManager.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.vltno.timeloop;
22

3+
import net.minecraft.world.phys.Vec3;
4+
35
import java.util.ArrayList;
46
import java.util.HashMap;
57
import java.util.List;
@@ -10,8 +12,6 @@ public class LoopSceneManager {
1012
private TimeLoopConfig config;
1113
private String scenePrefix;
1214
private Map<String, PlayerData> recordingPlayers;
13-
14-
1515

1616
// Constructor to initialize recordingPlayers map
1717
public LoopSceneManager(TimeLoopConfig config) {
@@ -23,18 +23,46 @@ public LoopSceneManager(TimeLoopConfig config) {
2323
// Method to add a player to the recordingPlayers map
2424
public void addPlayer(List<String> args) {
2525
String playerName = args.get(0);
26-
List<String> nickname = args.size() > 1 ? args.subList(1, args.size()) : null;
27-
List<String> skin = args.size() > 2 ? args.subList(2, args.size()) : null;
2826

29-
if (playerName != null && !playerName.isEmpty()) {
30-
String tempNickname = (nickname == null || nickname.isEmpty()) ? playerName : nickname.getFirst();
31-
String tempSkin = (skin == null || skin.isEmpty()) ? playerName : skin.getFirst();
32-
33-
// Use player name as the key and store a PlayerData object
34-
recordingPlayers.put(playerName, new PlayerData(playerName, tempNickname, tempSkin));
35-
} else {
36-
System.out.println("Invalid player data. Player not added.");
27+
String nickname = args.size() > 1 ? args.get(1) : null;
28+
String skin = args.size() > 2 ? args.get(2) : null;
29+
String rewindPosition = args.size() > 3 ? args.get(3) : null;
30+
31+
if (playerName == null || playerName.isEmpty()) {
32+
TimeLoop.LOOP_LOGGER.error("Player name is null or empty. Skipping player addition.");
33+
return;
3734
}
35+
36+
String tempNickname = (nickname == null || nickname.isEmpty()) ? playerName : nickname;
37+
String tempSkin = (skin == null || skin.isEmpty()) ? playerName : skin;
38+
39+
Vec3 tempRewindPosition = Vec3.ZERO;
40+
41+
if (rewindPosition != null && !rewindPosition.isEmpty()) {
42+
try {
43+
String processedString = rewindPosition.trim();
44+
45+
if ((processedString.startsWith("(") && processedString.endsWith(")")) ||
46+
(processedString.startsWith("[") && processedString.endsWith("]"))) {
47+
processedString = processedString.substring(1, processedString.length() - 1).trim();
48+
}
49+
50+
String[] positionParts = processedString.split("\\s*,\\s*");
51+
52+
if (positionParts.length == 3) {
53+
double x = Double.parseDouble(positionParts[0]);
54+
double y = Double.parseDouble(positionParts[1]);
55+
double z = Double.parseDouble(positionParts[2]);
56+
tempRewindPosition = new Vec3(x, y, z);
57+
}
58+
} catch (NumberFormatException e) {
59+
TimeLoop.LOOP_LOGGER.error("Invalid rewind position format. Skipping rewind position.");
60+
}
61+
}
62+
63+
PlayerData playerData = new PlayerData(playerName, tempNickname, tempSkin, tempRewindPosition);
64+
65+
recordingPlayers.put(playerName, playerData);
3866
}
3967

4068

@@ -43,12 +71,12 @@ public void removePlayer(String playerName) {
4371
if (playerName != null && !playerName.isEmpty()) {
4472
PlayerData removed = recordingPlayers.remove(playerName);
4573
if (removed != null) {
46-
System.out.println("Player '" + playerName + "' removed successfully.");
74+
TimeLoop.LOOP_LOGGER.info("Player '" + playerName + "' removed successfully.");
4775
} else {
48-
System.out.println("Player '" + playerName + "' not found in the list.");
76+
TimeLoop.LOOP_LOGGER.info("Player '" + playerName + "' not found in the list.");
4977
}
5078
} else {
51-
System.out.println("Invalid player name. Player not removed.");
79+
TimeLoop.LOOP_LOGGER.info("Invalid player name. Player not removed.");
5280
}
5381
}
5482

common/src/main/java/com/vltno/timeloop/PlayerData.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
package com.vltno.timeloop;
22

3+
import net.minecraft.world.phys.Vec3;
4+
35
public class PlayerData {
46
private String name;
57
private String nickname;
68
private String skin;
7-
8-
public PlayerData(String name, String nickname, String skin) {
9+
private Vec3 startPosition;
10+
private Vec3 joinPosition;
11+
12+
public PlayerData(String name, String nickname, String skin, Vec3 joinPosition) {
913
this.name = name;
1014
this.nickname = nickname;
1115
this.skin = skin;
16+
this.startPosition = null;
17+
this.joinPosition = joinPosition;
1218
}
1319

1420
// Getters and setters for player attributes
@@ -32,12 +38,30 @@ public void setSkin(String skin) {
3238
this.skin = skin;
3339
}
3440

41+
public Vec3 getStartPosition() {
42+
return startPosition;
43+
}
44+
45+
public void setStartPosition(Vec3 newStartPosition) {
46+
this.startPosition = newStartPosition;
47+
}
48+
49+
public Vec3 getJoinPosition() {
50+
return joinPosition;
51+
}
52+
53+
public void setJoinPosition(Vec3 newJoinPosition) {
54+
this.joinPosition = newJoinPosition;
55+
}
56+
3557
@Override
3658
public String toString() {
3759
return "PlayerData{" +
3860
"name='" + name + '\'' +
3961
", nickname='" + nickname + '\'' +
4062
", skin='" + skin + '\'' +
63+
", join-position='" + joinPosition + '\'' +
64+
", start-position='" + startPosition + '\'' +
4165
'}';
4266
}
4367
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.vltno.timeloop;
2+
3+
import net.minecraft.util.StringRepresentable;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
public enum RewindTypes implements StringRepresentable {
7+
NONE,
8+
START_POSITION,
9+
JOIN_POSITION;
10+
11+
@Override
12+
public @NotNull String getSerializedName() {
13+
return name();
14+
}
15+
}

common/src/main/java/com/vltno/timeloop/TimeLoop.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import net.minecraft.commands.Commands;
99
import net.minecraft.server.MinecraftServer;
1010
import net.minecraft.server.level.ServerLevel;
11+
import net.minecraft.world.entity.player.Player;
12+
import net.minecraft.world.phys.Vec3;
1113
import org.slf4j.Logger;
1214
import org.slf4j.LoggerFactory;
1315

@@ -41,6 +43,7 @@ public class TimeLoop {
4143
public static LoopTypes loopType;
4244
public static boolean trackChat;
4345
public static boolean hurtLoopedPlayers;
46+
public static RewindTypes rewindType;
4447

4548
// The configuration object loaded from disk
4649
public static TimeLoopConfig config;
@@ -104,7 +107,29 @@ public static void runLoopIteration() {
104107
String playerName = playerData.getName();
105108
String playerNickname = playerData.getNickname();
106109
String playerSkin = playerData.getSkin();
107-
110+
Vec3 startPosition = playerData.getStartPosition();
111+
Vec3 joinPosition = playerData.getJoinPosition();
112+
113+
Player player = server.getPlayerList().getPlayerByName(playerName);
114+
115+
switch (rewindType) {
116+
case START_POSITION -> {
117+
if (startPosition == null) {
118+
LOOP_LOGGER.error("Player {} has no start position yet. Defaulting to join position.", playerName);
119+
player.teleportTo(joinPosition.x, joinPosition.y, joinPosition.z);
120+
return;
121+
}
122+
player.teleportTo(startPosition.x, startPosition.y, startPosition.z);
123+
}
124+
case JOIN_POSITION -> {
125+
if (joinPosition == null) {
126+
LOOP_LOGGER.error("Player {} has no join position yet. (somehow??)", playerName);
127+
return;
128+
}
129+
player.teleportTo(joinPosition.x, joinPosition.y, joinPosition.z);
130+
}
131+
}
132+
108133
String playerSceneName = loopSceneManager.getPlayerSceneName(playerName);
109134
TimeLoop.executeCommand(String.format("mocap playback start .%s %s skin_from_player %s", playerSceneName, playerNickname, playerSkin));
110135
});
@@ -126,7 +151,11 @@ public static void startLoop() {
126151
if (showLoopInfo) {
127152
loopBossBar.visible(loopType.equals(LoopTypes.TICKS) || loopType.equals(LoopTypes.TIME_OF_DAY));
128153
}
129-
154+
155+
loopSceneManager.forEachRecordingPlayer(playerData -> {
156+
playerData.setStartPosition(server.getPlayerList().getPlayerByName(playerData.getName()).position());
157+
});
158+
130159
isLooping = true;
131160
config.isLooping = true;
132161
tickCounter = 0;

common/src/main/java/com/vltno/timeloop/TimeLoopConfig.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@
1212
import java.util.Map;
1313

1414
public class TimeLoopConfig {
15-
// These values will be loaded/saved from/to the JSON config file.bossbar set minecraft:loop_info
1615
public String scenePrefix = "loop_scene";
1716
public boolean firstStart = true;
18-
public int loopIteration = 1;
17+
public int loopIteration = 0;
1918
public boolean isLooping = false;
2019
public int loopLengthTicks = 6000; // Default: 6000 ticks (i.e. 5 minutes)
2120
public int maxLoops = 0; // No limit by default
@@ -30,6 +29,7 @@ public class TimeLoopConfig {
3029
public LoopTypes loopType = LoopTypes.TICKS;
3130
public boolean trackChat = false;
3231
public boolean hurtLoopedPlayers = false;
32+
public RewindTypes rewindType = RewindTypes.NONE;
3333

3434
public Map<String, PlayerData> recordingPlayers = new HashMap<>();
3535

@@ -41,7 +41,7 @@ public class TimeLoopConfig {
4141
* If the file does not exist, a default config is created and saved.
4242
*
4343
* @param configDir the config directory (usually obtained from FabricLoader)
44-
* @return an instance of LoopConfig
44+
* @return an instance of TimeLoopConfig
4545
*/
4646
public static TimeLoopConfig load(Path configDir) {
4747
configPath = configDir.resolve("timeloop.json");

common/src/main/java/com/vltno/timeloop/commands/BaseCommands.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.vltno.timeloop.LoopCommands;
66
import com.vltno.timeloop.LoopTypes;
77
import com.vltno.timeloop.TimeLoop;
8+
import com.vltno.timeloop.TimeLoopConfig;
89
import net.minecraft.commands.Commands;
910
import net.minecraft.commands.CommandSourceStack;
1011
import net.minecraft.network.chat.Component;
@@ -110,24 +111,26 @@ private static int reset(CommandContext<CommandSourceStack> context) {
110111
CommandSourceStack source = context.getSource();
111112
TimeLoop.stopLoop();
112113

113-
// Reset logic remains the same internally
114-
TimeLoop.startTimeOfDay = 0;
115-
TimeLoop.config.startTimeOfDay = 0;
114+
// Reset logic
115+
TimeLoopConfig baseConfig = new TimeLoopConfig();
116+
117+
TimeLoop.startTimeOfDay = baseConfig.startTimeOfDay;
118+
TimeLoop.config.startTimeOfDay = baseConfig.startTimeOfDay;
116119

117-
TimeLoop.timeSetting = 13000;
118-
TimeLoop.config.timeSetting = 13000;
120+
TimeLoop.timeSetting = baseConfig.timeSetting;
121+
TimeLoop.config.timeSetting = baseConfig.timeSetting;
119122

120123
TimeLoop.ticksLeft = TimeLoop.loopLengthTicks;
121124
TimeLoop.config.ticksLeft = TimeLoop.config.loopLengthTicks;
122125

123-
TimeLoop.trackItems = false;
124-
TimeLoop.config.trackItems = false;
126+
TimeLoop.trackItems = baseConfig.trackItems;
127+
TimeLoop.config.trackItems = baseConfig.trackItems;
125128

126-
TimeLoop.loopType = LoopTypes.TICKS;
127-
TimeLoop.config.loopType = LoopTypes.TICKS;
129+
TimeLoop.loopType = baseConfig.loopType;
130+
TimeLoop.config.loopType = baseConfig.loopType;
128131

129-
TimeLoop.displayTimeInTicks = false;
130-
TimeLoop.config.displayTimeInTicks = false;
132+
TimeLoop.displayTimeInTicks = baseConfig.displayTimeInTicks;
133+
TimeLoop.config.displayTimeInTicks = baseConfig.displayTimeInTicks;
131134

132135
TimeLoop.executeCommand("mocap playback stop_all");
133136
TimeLoop.loopSceneManager.forEachPlayerSceneName(playerSceneName -> {

0 commit comments

Comments
 (0)