Skip to content

Commit 9961986

Browse files
committed
Improve gamerule mirroring, add new dimension type that uses overworld clocks and timelines, add method for easier setup of clock times
1 parent bda0ec7 commit 9961986

10 files changed

Lines changed: 206 additions & 17 deletions

File tree

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ org.gradle.jvmargs=-Xmx2G
33

44
# Fabric Properties
55

6-
minecraft_version=26.1.1
6+
minecraft_version=26.1.2
77
loader_version=0.18.5
88
loom_version=1.15-SNAPSHOT
99

1010
# Mod Properties
11-
mod_version=0.8.0-beta.1
11+
mod_version=0.8.0
1212
maven_group=xyz.nucleoid
1313
archives_base_name=fantasy
1414

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package xyz.nucleoid.fantasy;
2+
3+
import com.google.common.collect.Streams;
4+
import net.minecraft.server.MinecraftServer;
5+
import net.minecraft.world.flag.FeatureFlagSet;
6+
import net.minecraft.world.level.gamerules.GameRule;
7+
import net.minecraft.world.level.gamerules.GameRuleMap;
8+
import net.minecraft.world.level.gamerules.GameRules;
9+
import org.jspecify.annotations.Nullable;
10+
import xyz.nucleoid.fantasy.mixin.GameRulesAccessor;
11+
12+
import java.util.List;
13+
import java.util.stream.Stream;
14+
15+
class DelegatingGameRules extends GameRules {
16+
private final GameRules parent;
17+
private final GameRuleMap rules;
18+
19+
public DelegatingGameRules(GameRules parent) {
20+
super(List.of());
21+
this.parent = parent;
22+
this.rules = ((GameRulesAccessor) this).getRules();
23+
}
24+
25+
@Override
26+
public <T> void set(GameRule<T> gameRule, T value, @Nullable MinecraftServer server) {
27+
this.rules.set(gameRule, value);
28+
if (server != null) {
29+
server.onGameRuleChanged(gameRule, value);
30+
}
31+
}
32+
33+
@Override
34+
public <T> T get(GameRule<T> gameRule) {
35+
T value = this.rules.get(gameRule);
36+
if (value == null) {
37+
return this.parent.get(gameRule);
38+
} else {
39+
return value;
40+
}
41+
}
42+
43+
@Override
44+
public Stream<GameRule<?>> availableRules() {
45+
return Streams.concat(this.rules.keySet().stream(), this.parent.availableRules());
46+
}
47+
48+
@Override
49+
public GameRules copy(FeatureFlagSet enabledFeatures) {
50+
var copy = new DelegatingGameRules(this.parent);
51+
this.setAll(this, null);
52+
return copy;
53+
}
54+
}

src/main/java/xyz/nucleoid/fantasy/Fantasy.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@
1010
import net.minecraft.server.MinecraftServer;
1111
import net.minecraft.server.level.ServerLevel;
1212
import net.minecraft.server.level.ServerPlayer;
13-
import net.minecraft.tags.TagKey;
1413
import net.minecraft.world.clock.WorldClock;
1514
import net.minecraft.world.level.Level;
1615
import net.minecraft.world.level.dimension.DimensionType;
1716
import net.minecraft.world.level.portal.TeleportTransition;
1817
import net.minecraft.world.level.storage.LevelData;
1918
import net.minecraft.world.level.storage.LevelStorageSource;
2019
import net.minecraft.world.phys.Vec3;
21-
import net.minecraft.world.timeline.Timeline;
2220
import org.apache.commons.io.FileUtils;
2321
import org.apache.commons.lang3.RandomStringUtils;
2422
import org.apache.logging.log4j.LogManager;
@@ -42,7 +40,16 @@
4240
public final class Fantasy {
4341
public static final Logger LOGGER = LogManager.getLogger(Fantasy.class);
4442
public static final String ID = "fantasy";
43+
/**
44+
* Default builtin dimension, using its own timelines and world clocks {@link #DEFAULT_CLOCK}.
45+
*/
4546
public static final ResourceKey<DimensionType> DEFAULT_DIM_TYPE = ResourceKey.create(Registries.DIMENSION_TYPE, Identifier.fromNamespaceAndPath(Fantasy.ID, "default"));
47+
public static final ResourceKey<WorldClock> DEFAULT_CLOCK = ResourceKey.create(Registries.WORLD_CLOCK, Identifier.fromNamespaceAndPath(Fantasy.ID, "default"));
48+
49+
/**
50+
* Default builtin dimension, but using overworld's timelines and world clock {@link net.minecraft.world.clock.WorldClocks#OVERWORLD}.
51+
*/
52+
public static final ResourceKey<DimensionType> DEFAULT_OVERWORLD_DIM_TYPE = ResourceKey.create(Registries.DIMENSION_TYPE, Identifier.fromNamespaceAndPath(Fantasy.ID, "default_overworld"));
4653
private static Fantasy instance;
4754

4855
private final MinecraftServer server;

src/main/java/xyz/nucleoid/fantasy/RuntimeLevel.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ public class RuntimeLevel extends ServerLevel {
3131
protected RuntimeLevel(MinecraftServer server, ResourceKey<Level> dimension, RuntimeLevelConfig config, Style style) {
3232
LevelStem dimensionOptions = config.createDimensionOptions(server);
3333
GameRules gameRules = null;
34-
if (!config.shouldMirrorOverworldGameRules()) {
34+
if (!config.getGameRules().isEmpty() && config.shouldMirrorOverworldGameRules()) {
35+
gameRules = new DelegatingGameRules(server.getGameRules());
36+
} else if (!config.shouldMirrorOverworldGameRules()) {
3537
gameRules = new GameRules(server.getWorldData().enabledFeatures());
38+
}
39+
40+
if (gameRules != null) {
3641
config.getGameRules().applyTo(gameRules, null);
37-
} else {
38-
gameRules = null;
3942
}
4043

4144
this.clockManager = config.getClockManager(server, gameRules != null ? gameRules : server.getGameRules());

src/main/java/xyz/nucleoid/fantasy/RuntimeLevelConfig.java

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
import net.minecraft.resources.ResourceKey;
88
import net.minecraft.server.MinecraftServer;
99
import net.minecraft.world.Difficulty;
10+
import net.minecraft.world.clock.ClockState;
1011
import net.minecraft.world.clock.PackedClockStates;
1112
import net.minecraft.world.clock.ServerClockManager;
13+
import net.minecraft.world.clock.WorldClock;
1214
import net.minecraft.world.level.chunk.ChunkGenerator;
1315
import net.minecraft.world.level.dimension.DimensionType;
1416
import net.minecraft.world.level.dimension.LevelStem;
@@ -17,6 +19,7 @@
1719
import org.jetbrains.annotations.Nullable;
1820
import xyz.nucleoid.fantasy.util.GameRuleStore;
1921

22+
import java.util.HashMap;
2023
import java.util.Map;
2124
import java.util.function.BiFunction;
2225
import java.util.function.BooleanSupplier;
@@ -40,7 +43,8 @@ public final class RuntimeLevelConfig {
4043
private boolean mirrorOverworldDifficulty = false;
4144
private boolean mirrorOverworldClocks = false;
4245
private RuntimeLevel.Constructor levelConstructor = RuntimeLevel::new;
43-
private Function<BooleanSupplier, RuntimeClockManager> clockManagerConstructor = s -> new RuntimeClockManager(PackedClockStates.EMPTY, s);
46+
private BiFunction<PackedClockStates, BooleanSupplier, RuntimeClockManager> clockManagerConstructor = (c, s) -> new RuntimeClockManager(c, s);
47+
private final Map<ResourceKey<WorldClock>, ClockState> clockState = new HashMap<>();
4448

4549
private long gameTime = 0;
4650
private TriState flat = TriState.DEFAULT;
@@ -77,7 +81,7 @@ public RuntimeLevelConfig setLevelConstructor(RuntimeLevel.Constructor construct
7781
* @return The same instance of {@link RuntimeLevelConfig}
7882
*/
7983
public RuntimeLevelConfig setClockManagerConstructor(PackedClockStates clockStates) {
80-
this.clockManagerConstructor = s -> new RuntimeClockManager(clockStates, s);
84+
this.clockManagerConstructor = (_, s) -> new RuntimeClockManager(clockStates, s);
8185
return this;
8286
}
8387

@@ -89,10 +93,39 @@ public RuntimeLevelConfig setClockManagerConstructor(PackedClockStates clockStat
8993
* @return The same instance of {@link RuntimeLevelConfig}
9094
*/
9195
public RuntimeLevelConfig setClockManagerConstructor(Function<BooleanSupplier, RuntimeClockManager> clockManagerConstructor) {
96+
this.clockManagerConstructor = (_, s) -> clockManagerConstructor.apply(s);
97+
return this;
98+
}
99+
100+
/**
101+
* Sets the clock manager constructor
102+
*
103+
* @param clockManagerConstructor The clock manager constructor to use
104+
*
105+
* @return The same instance of {@link RuntimeLevelConfig}
106+
*/
107+
public RuntimeLevelConfig setClockManagerConstructor(BiFunction<PackedClockStates, BooleanSupplier, RuntimeClockManager> clockManagerConstructor) {
92108
this.clockManagerConstructor = clockManagerConstructor;
93109
return this;
94110
}
95111

112+
public RuntimeLevelConfig setClockTime(ResourceKey<WorldClock> clock, int time) {
113+
return this.setClockTime(clock, new ClockState(time, 0, 1, false));
114+
}
115+
116+
public RuntimeLevelConfig setClockTime(ResourceKey<WorldClock> clock, int time, float rate) {
117+
return this.setClockTime(clock, new ClockState(time, 0, rate, false));
118+
}
119+
120+
public RuntimeLevelConfig setClockTime(ResourceKey<WorldClock> clock, int time, boolean paused) {
121+
return this.setClockTime(clock, new ClockState(time, 0, 1, paused));
122+
}
123+
124+
public RuntimeLevelConfig setClockTime(ResourceKey<WorldClock> clock, ClockState state) {
125+
this.clockState.put(clock, state);
126+
return this;
127+
}
128+
96129
/**
97130
* Sets the level dimension type
98131
*
@@ -193,8 +226,6 @@ public RuntimeLevelConfig setDifficulty(Difficulty difficulty) {
193226

194227
/**
195228
* Modifies a gamerule
196-
* <br/>
197-
* <b>Does nothing if {@link RuntimeLevelConfig#mirrorOverworldGameRules} is true</b>
198229
*
199230
* @param key The gamerule to modify
200231
* @param value The value of the gamerule
@@ -242,6 +273,13 @@ public RuntimeLevelConfig setMirrorOverworldClocks(boolean mirror) {
242273
return this;
243274
}
244275

276+
public RuntimeLevelConfig mirrorOverworldGameState() {
277+
this.setMirrorOverworldGameRules(true);
278+
this.setMirrorOverworldClocks(true);
279+
this.setMirrorOverworldDifficulty(true);
280+
return this;
281+
}
282+
245283
/**
246284
* Defines if the level is a flat level or not
247285
*
@@ -352,8 +390,16 @@ public ServerClockManager getClockManager(MinecraftServer server, GameRules game
352390
if (this.mirrorOverworldClocks) {
353391
return server.clockManager();
354392
}
393+
PackedClockStates states = PackedClockStates.EMPTY;
394+
if (!this.clockState.isEmpty()) {
395+
var map = new HashMap<Holder<WorldClock>, ClockState>();
396+
for (var entry : this.clockState.entrySet()) {
397+
map.put(server.registryAccess().getOrThrow(entry.getKey()), entry.getValue());
398+
}
399+
states = new PackedClockStates(map);
400+
}
355401

356-
var t = this.clockManagerConstructor.apply(() -> gameRules.get(GameRules.ADVANCE_TIME));
402+
var t = this.clockManagerConstructor.apply(states, () -> gameRules.get(GameRules.ADVANCE_TIME));
357403
t.init(server);
358404

359405
return t;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package xyz.nucleoid.fantasy.mixin;
2+
3+
import net.minecraft.world.level.gamerules.GameRuleMap;
4+
import org.spongepowered.asm.mixin.gen.Accessor;
5+
6+
@org.spongepowered.asm.mixin.Mixin(net.minecraft.world.level.gamerules.GameRules.class)
7+
public interface GameRulesAccessor {
8+
@Accessor
9+
GameRuleMap getRules();
10+
}

src/main/java/xyz/nucleoid/fantasy/util/GameRuleStore.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ public <T> void set(GameRule<T> key, T value) {
1313
this.rules.put(key, value);
1414
}
1515

16-
17-
1816
public <T>T get(GameRule<T> key) {
1917
return (T) this.rules.get(key);
2018
}
@@ -23,6 +21,10 @@ public boolean contains(GameRule<?> key) {
2321
return this.rules.containsKey(key);
2422
}
2523

24+
public boolean isEmpty() {
25+
return this.rules.isEmpty();
26+
}
27+
2628
public void applyTo(GameRules rules, @Nullable MinecraftServer server) {
2729
Reference2ObjectMaps.fastForEach(this.rules, entry -> {
2830
//noinspection unchecked
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"ambient_light": 0,
3+
"attributes": {
4+
"minecraft:audio/ambient_sounds": {
5+
"mood": {
6+
"block_search_extent": 8,
7+
"offset": 2,
8+
"sound": "minecraft:ambient.cave",
9+
"tick_delay": 6000
10+
}
11+
},
12+
"minecraft:audio/background_music": {
13+
"creative": {
14+
"max_delay": 24000,
15+
"min_delay": 12000,
16+
"sound": "minecraft:music.creative"
17+
},
18+
"default": {
19+
"max_delay": 24000,
20+
"min_delay": 12000,
21+
"sound": "minecraft:music.game"
22+
}
23+
},
24+
"minecraft:gameplay/bed_rule": {
25+
"can_set_spawn": "always",
26+
"can_sleep": "when_dark",
27+
"error_message": {
28+
"translate": "block.minecraft.bed.no_sleep"
29+
}
30+
},
31+
"minecraft:gameplay/nether_portal_spawns_piglin": true,
32+
"minecraft:gameplay/respawn_anchor_works": false,
33+
"minecraft:visual/ambient_light_color": "#0a0a0a",
34+
"minecraft:visual/cloud_color": "#ccffffff",
35+
"minecraft:visual/cloud_height": 192.33,
36+
"minecraft:visual/fog_color": "#c0d8ff",
37+
"minecraft:visual/sky_color": "#78a7ff"
38+
},
39+
"coordinate_scale": 1,
40+
"default_clock": "minecraft:overworld",
41+
"has_ceiling": false,
42+
"has_ender_dragon_fight": false,
43+
"has_skylight": true,
44+
"height": 256,
45+
"infiniburn": "#minecraft:infiniburn_overworld",
46+
"logical_height": 256,
47+
"min_y": 0,
48+
"monster_spawn_block_light_limit": 0,
49+
"monster_spawn_light_level": {
50+
"type": "minecraft:uniform",
51+
"max_inclusive": 7,
52+
"min_inclusive": 0
53+
},
54+
"timelines": "#minecraft:in_overworld"
55+
}

src/main/resources/fantasy.mixins.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
"compatibilityLevel": "JAVA_25",
66
"mixins": [
77
"ChunkMapMixin",
8-
"clock.ClockInstanceAccessor",
8+
"GameRulesAccessor",
99
"MinecraftServerAccess",
1010
"MinecraftServerMixin",
11-
"clock.PlayerListMixin",
1211
"ServerChunkCacheMixin",
13-
"clock.ServerClockManagerMixin",
1412
"ServerLevelMixin",
1513
"TimeCommandMixin",
14+
"clock.ClockInstanceAccessor",
15+
"clock.PlayerListMixin",
16+
"clock.ServerClockManagerMixin",
1617
"registry.LevelStemMixin",
1718
"registry.MappedRegistryMixin",
1819
"registry.WorldGenSettingsMixin"

src/testmod/java/xyz/nucleoid/fantasy/test/FantasyInitializer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.minecraft.core.registries.Registries;
1010
import net.minecraft.network.chat.Component;
1111
import net.minecraft.resources.Identifier;
12+
import net.minecraft.world.clock.WorldClocks;
1213
import net.minecraft.world.level.biome.Biomes;
1314
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
1415
import net.minecraft.world.level.gamerules.GameRules;
@@ -45,6 +46,16 @@ public void onInitialize() {
4546
new RuntimeLevelConfig().setGenerator(s.overworld().getChunkSource().getGenerator()).setLevelConstructor(CustomLevel::new)
4647
);
4748

49+
Fantasy.get(s).openTemporaryLevel(
50+
Identifier.fromNamespaceAndPath("fantasy_test", "temp_overworld_timed"),
51+
new RuntimeLevelConfig().setGenerator(s.overworld().getChunkSource().getGenerator())
52+
.setDimensionType(Fantasy.DEFAULT_OVERWORLD_DIM_TYPE)
53+
.setMirrorOverworldGameRules(true)
54+
.setGameRule(GameRules.BLOCK_DROPS, false)
55+
.setClockTime(WorldClocks.OVERWORLD, 1200, true)
56+
.setLevelConstructor(CustomLevel::new)
57+
);
58+
4859
var biome = s.registryAccess().lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS);
4960
var flat = new FlatLevelGeneratorSettings(Optional.empty(), biome, List.of());
5061
var generator = new FlatLevelSource(flat);

0 commit comments

Comments
 (0)