1
1
package io .izzel .arclight .common .mixin .core .server .level ;
2
2
3
3
import com .google .common .collect .Lists ;
4
+ import io .izzel .arclight .common .bridge .bukkit .CraftServerBridge ;
4
5
import io .izzel .arclight .common .bridge .core .entity .EntityBridge ;
5
6
import io .izzel .arclight .common .bridge .core .entity .player .ServerPlayerEntityBridge ;
6
7
import io .izzel .arclight .common .bridge .core .inventory .IInventoryBridge ;
8
+ import io .izzel .arclight .common .bridge .core .server .MinecraftServerBridge ;
7
9
import io .izzel .arclight .common .bridge .core .world .ExplosionBridge ;
8
10
import io .izzel .arclight .common .bridge .core .world .server .ServerChunkProviderBridge ;
9
11
import io .izzel .arclight .common .bridge .core .world .server .ServerWorldBridge ;
45
47
import net .minecraft .world .level .CustomSpawner ;
46
48
import net .minecraft .world .level .Explosion ;
47
49
import net .minecraft .world .level .Level ;
48
- import net .minecraft .world .level .LevelAccessor ;
50
+ import net .minecraft .world .level .biome . BiomeSource ;
49
51
import net .minecraft .world .level .block .Block ;
50
52
import net .minecraft .world .level .block .entity .BlockEntity ;
51
53
import net .minecraft .world .level .block .state .BlockState ;
54
+ import net .minecraft .world .level .chunk .ChunkGenerator ;
52
55
import net .minecraft .world .level .chunk .LevelChunk ;
53
56
import net .minecraft .world .level .dimension .LevelStem ;
54
57
import net .minecraft .world .level .entity .PersistentEntitySectionManager ;
55
58
import net .minecraft .world .level .gameevent .GameEvent ;
59
+ import net .minecraft .world .level .levelgen .FlatLevelSource ;
60
+ import net .minecraft .world .level .levelgen .NoiseBasedChunkGenerator ;
56
61
import net .minecraft .world .level .saveddata .maps .MapId ;
57
62
import net .minecraft .world .level .saveddata .maps .MapItemSavedData ;
58
- import net .minecraft .world .level .storage .DerivedLevelData ;
59
- import net .minecraft .world .level .storage .DimensionDataStorage ;
60
- import net .minecraft .world .level .storage .LevelStorageSource ;
61
- import net .minecraft .world .level .storage .PrimaryLevelData ;
62
- import net .minecraft .world .level .storage .ServerLevelData ;
63
+ import net .minecraft .world .level .storage .*;
63
64
import net .minecraft .world .phys .Vec3 ;
64
65
import org .bukkit .Bukkit ;
65
66
import org .bukkit .Location ;
67
+ import org .bukkit .World ;
66
68
import org .bukkit .craftbukkit .v .entity .CraftHumanEntity ;
67
69
import org .bukkit .craftbukkit .v .event .CraftEventFactory ;
68
70
import org .bukkit .craftbukkit .v .generator .CustomChunkGenerator ;
71
+ import org .bukkit .craftbukkit .v .generator .CustomWorldChunkManager ;
69
72
import org .bukkit .craftbukkit .v .util .CraftNamespacedKey ;
70
73
import org .bukkit .craftbukkit .v .util .WorldUUID ;
71
74
import org .bukkit .entity .HumanEntity ;
@@ -124,32 +127,75 @@ public ResourceKey<LevelStem> getTypeKey() {
124
127
return this .typeKey ;
125
128
}
126
129
127
- // Multiworld support: per-world seed storage
128
- @ Inject (method = "getSeed" , at = @ At ("HEAD" ), cancellable = true )
129
- private void arclight$getSeed (CallbackInfoReturnable <Long > cir ) {
130
- if (K != null ) {
131
- cir .setReturnValue (K .worldGenOptions ().seed ());
132
- }
133
- }
134
-
135
130
@ ShadowConstructor
136
131
public void arclight$constructor (MinecraftServer minecraftServer , Executor backgroundExecutor , LevelStorageSource .LevelStorageAccess levelSave , ServerLevelData worldInfo , ResourceKey <Level > dimension , LevelStem levelStem , ChunkProgressListener statusListener , boolean isDebug , long seed , List <CustomSpawner > specialSpawners , boolean shouldBeTicking , RandomSequences seq ) {
137
132
throw new RuntimeException ();
138
133
}
139
134
140
135
@ CreateConstructor
141
- public void arclight$constructor (MinecraftServer minecraftServer , Executor backgroundExecutor , LevelStorageSource .LevelStorageAccess levelSave , PrimaryLevelData worldInfo , ResourceKey <Level > dimension , LevelStem levelStem , ChunkProgressListener statusListener , boolean isDebug , long seed , List <CustomSpawner > specialSpawners , boolean shouldBeTicking , RandomSequences seq , org .bukkit .World .Environment env , org .bukkit .generator .ChunkGenerator gen , org .bukkit .generator .BiomeProvider biomeProvider ) {
142
- arclight$constructor ( minecraftServer , backgroundExecutor , levelSave , worldInfo , dimension , levelStem , statusListener , isDebug , seed , specialSpawners , shouldBeTicking , seq );
143
- this . generator = gen ;
144
- this . environment = env ;
145
- this . biomeProvider = biomeProvider ;
146
- if ( gen != null ) {
147
- CustomChunkGenerator generator = new CustomChunkGenerator (( ServerLevel ) ( Object ) this , this . chunkSource . getGenerator (), gen );
148
- (( ServerChunkProviderBridge ) this . chunkSource ). bridge$setChunkGenerator ( generator );
149
- }
136
+ public void arclight$constructor (MinecraftServer server , Executor backgroundExecutor , LevelStorageSource .LevelStorageAccess levelSave , PrimaryLevelData worldInfo , ResourceKey <Level > dimension , LevelStem levelStem , ChunkProgressListener statusListener , boolean isDebug , long seed , List <CustomSpawner > specialSpawners , boolean shouldBeTicking , RandomSequences seq , org .bukkit .World .Environment env , org .bukkit .generator .ChunkGenerator gen , org .bukkit .generator .BiomeProvider biomeProvider ) {
137
+ var craftBridge = ( CraftServerBridge )( Object ) (( MinecraftServerBridge ) server ). bridge$getServer ( );
138
+ assert craftBridge != null ; // Already checked in bridge
139
+ // We have no way but store it somewhere and use a default value
140
+ // in order to avoid having to pass them as arguments.
141
+ craftBridge . bridge$offerGeneratorCache ( worldInfo . getLevelName (), gen );
142
+ craftBridge . bridge$offerBiomeProviderCache ( worldInfo . getLevelName (), biomeProvider );
143
+ // Wrap the
144
+ arclight$constructor ( server , backgroundExecutor , levelSave , worldInfo , dimension , levelStem , statusListener , isDebug , seed , specialSpawners , shouldBeTicking , seq );
150
145
bridge$getWorld ();
151
146
}
152
147
148
+ // Support custom chunk generator; in consistency with CraftBukkit
149
+ // The real part is inside ServerChunkCache, when initializing ChunkMap (in ctor),
150
+ // where a generator state is created, which is later used for chunk generation.
151
+ // Previously we didn't modify it before ChunkMap is created,
152
+ // which in turn cause custom world generation from Bukkit failing to work.
153
+ @ Decorate (method = "<init>" , at = @ At (value = "INVOKE" , target = "Lnet/minecraft/world/level/dimension/LevelStem;generator()Lnet/minecraft/world/level/chunk/ChunkGenerator;" ))
154
+ private ChunkGenerator arclight$initChunkGenerator (LevelStem instance , @ Local (ordinal = -1 ) MinecraftServer server , @ Local (ordinal = -1 ) ServerLevelData worldInfo ) throws Throwable {
155
+ // Pulling up world info init since level info is used when selecting ChunkGenerator.
156
+ if (worldInfo instanceof PrimaryLevelData primary ) {
157
+ ((WorldInfoBridge ) primary ).bridge$setWorld ((ServerLevel ) (Object ) this );
158
+ this .K = primary ;
159
+ } else {
160
+ ArclightServer .LOGGER .warn ("Level {} isn't initialized with PrimaryLevelData." , this .serverLevelData .getLevelName ());
161
+ // damn spigot again
162
+ this .K = DelegateWorldInfo .wrap (worldInfo );
163
+ }
164
+
165
+ var craftBridge = (CraftServerBridge ) (Object ) ((MinecraftServerBridge ) server ).bridge$getServer ();
166
+ this .biomeProvider = craftBridge .bridge$consumeBiomeProviderCache (worldInfo .getLevelName ());
167
+ this .generator = craftBridge .bridge$consumeGeneratorCache (worldInfo .getLevelName ());
168
+
169
+ if (instance .type () == LevelStem .OVERWORLD ) {
170
+ this .environment = World .Environment .NORMAL ;
171
+ } else if (instance .type () == LevelStem .NETHER ) {
172
+ this .environment = World .Environment .NETHER ;
173
+ } else if (instance .type () == LevelStem .END ) {
174
+ this .environment = World .Environment .THE_END ;
175
+ } else {
176
+ this .environment = World .Environment .CUSTOM ;
177
+ }
178
+ // Data needed by getWorld() are all initialized for possible creating CraftWorld.
179
+ // CraftBukkit start: select custom chunk generator
180
+ ChunkGenerator raw = (ChunkGenerator ) DecorationOps .callsite ().invoke (instance );
181
+ if (biomeProvider != null ) {
182
+ BiomeSource biomeSource = new CustomWorldChunkManager (getWorld (), biomeProvider , getServer ().registryAccess ().registryOrThrow (Registries .BIOME ));
183
+ if (raw instanceof NoiseBasedChunkGenerator noise ) {
184
+ raw = new NoiseBasedChunkGenerator (biomeSource , noise .settings );
185
+ } else if (raw instanceof FlatLevelSource flat ) {
186
+ ArclightServer .LOGGER .warn ("Level {} is flat -- requested biome provider won't be satisfied." , this .serverLevelData .getLevelName ());
187
+ raw = new FlatLevelSource (flat .settings ());
188
+ } else {
189
+ ArclightServer .LOGGER .warn ("Level {} has customized generator -- requested biome provider won't be satisfied." , this .serverLevelData .getLevelName ());
190
+ }
191
+ }
192
+ if (generator != null ) {
193
+ raw = new CustomChunkGenerator ((ServerLevel )(Object ) this , raw , generator );
194
+ }
195
+ // CraftBukkit end
196
+ return raw ;
197
+ }
198
+
153
199
@ Inject (method = "<init>" , at = @ At ("RETURN" ))
154
200
private void arclight$init (MinecraftServer minecraftServer , Executor backgroundExecutor , LevelStorageSource .LevelStorageAccess levelSave , ServerLevelData worldInfo , ResourceKey <Level > dimension , LevelStem levelStem , ChunkProgressListener statusListener , boolean isDebug , long seed , List <CustomSpawner > specialSpawners , boolean shouldBeTicking , RandomSequences seq , CallbackInfo ci ) {
155
201
this .pvpMode = minecraftServer .isPvpAllowed ();
@@ -166,12 +212,6 @@ public ResourceKey<LevelStem> getTypeKey() {
166
212
ArclightServer .LOGGER .warn ("Assign {} to unknown level stem {}" , dimension .location (), levelStem );
167
213
this .typeKey = ResourceKey .create (Registries .LEVEL_STEM , dimension .location ());
168
214
}
169
- }
170
- if (worldInfo instanceof PrimaryLevelData data ) {
171
- this .K = data ;
172
- } else {
173
- // damn spigot again
174
- this .K = DelegateWorldInfo .wrap (worldInfo );
175
215
if (worldInfo instanceof DerivedLevelData data ) {
176
216
((DerivedWorldInfoBridge ) worldInfo ).bridge$setDimType (this .getTypeKey ());
177
217
if (ArclightConfig .spec ().getCompat ().isSymlinkWorld ()) {
@@ -182,11 +222,20 @@ public ResourceKey<LevelStem> getTypeKey() {
182
222
this .spigotConfig = new SpigotWorldConfig (worldInfo .getLevelName ());
183
223
this .uuid = WorldUUID .getUUID (levelSave .getDimensionPath (this .dimension ()).toFile ());
184
224
((ServerChunkProviderBridge ) this .chunkSource ).bridge$setViewDistance (spigotConfig .viewDistance );
225
+ ((ServerChunkProviderBridge ) this .chunkSource ).bridge$setSimulationDistance (spigotConfig .simulationDistance );
185
226
((WorldInfoBridge ) this .K ).bridge$setWorld ((ServerLevel ) (Object ) this );
186
227
var data = this .getDataStorage ().computeIfAbsent (LevelPersistentData .factory (), "bukkit_pdc" );
187
228
this .bridge$getWorld ().readBukkitValues (data .getTag ());
188
229
}
189
230
231
+ // Multiworld support: per-world seed storage
232
+ @ Inject (method = "getSeed" , at = @ At ("HEAD" ), cancellable = true )
233
+ private void arclight$getSeed (CallbackInfoReturnable <Long > cir ) {
234
+ if (K != null ) {
235
+ cir .setReturnValue (K .worldGenOptions ().seed ());
236
+ }
237
+ }
238
+
190
239
@ Inject (method = "saveLevelData" , at = @ At ("RETURN" ))
191
240
private void arclight$savePdc (CallbackInfo ci ) {
192
241
var data = this .getDataStorage ().computeIfAbsent (LevelPersistentData .factory (), "bukkit_pdc" );
0 commit comments