11package net .set .spawn .mod .mixin ;
22
3+ import com .llamalad7 .mixinextras .injector .ModifyExpressionValue ;
34import com .llamalad7 .mixinextras .injector .wrapoperation .*;
45import com .llamalad7 .mixinextras .sugar .*;
56import com .llamalad7 .mixinextras .sugar .ref .*;
1314import net .set .spawn .mod .interfaces .MinecraftServerExtended ;
1415import org .spongepowered .asm .mixin .*;
1516import org .spongepowered .asm .mixin .injection .*;
16- import org .spongepowered .asm .mixin .injection .callback .* ;
17+ import org .spongepowered .asm .mixin .injection .callback .CallbackInfo ;
1718
1819import java .util .Random ;
1920
@@ -33,7 +34,16 @@ public abstract class ServerPlayerEntityMixin {
3334 public abstract ServerWorld getServerWorld ();
3435
3536 @ WrapOperation (method = "method_21281" , at = @ At (value = "INVOKE" , target = "Ljava/util/Random;nextInt(I)I" ))
36- private int setSpawn (Random random , int bounds , Operation <Integer > original , @ Local (ordinal = 0 ) BlockPos worldSpawn , @ Local (ordinal = 0 ) int spawnRadius , @ Share ("seed" ) LocalRef <Seed > seed , @ Share ("originalRandomResult" ) LocalRef <Integer > originalRandomResult , @ Share ("newRandomValue" ) LocalRef <Integer > newRandomValue ) {
37+ private int setSpawn (
38+ Random random ,
39+ int bounds ,
40+ Operation <Integer > original ,
41+ @ Local (ordinal = 0 ) BlockPos worldSpawn ,
42+ @ Local (ordinal = 0 ) int spawnRadius ,
43+ @ Share ("seed" ) LocalRef <Seed > seed ,
44+ @ Share ("originalRandomResult" ) LocalRef <Integer > originalRandomResult ,
45+ @ Share ("newRandomValue" ) LocalRef <Integer > newRandomValue
46+ ) {
3747 int originalResult = original .call (random , bounds );
3848
3949 if (((MinecraftServerExtended ) this .server ).setspawnmod$shouldModifySpawn ()) {
@@ -52,7 +62,7 @@ private int setSpawn(Random random, int bounds, Operation<Integer> original, @Lo
5262 int xLocal = x - worldSpawn .getX () + spawnRadius ;
5363 int result = xLocal + (z - worldSpawn .getZ () + spawnRadius ) * spawnDiameter ;
5464
55- if (xLocal >=0 && xLocal < spawnDiameter && result >= 0 && result < bounds ) {
65+ if (xLocal >= 0 && xLocal < spawnDiameter && result >= 0 && result < bounds ) {
5666 // we save the original result in case the set spawn is invalid, see fallbackOnInvalidSpawn
5767 originalRandomResult .set (originalResult );
5868 newRandomValue .set (result );
@@ -64,8 +74,36 @@ private int setSpawn(Random random, int bounds, Operation<Integer> original, @Lo
6474 return originalResult ;
6575 }
6676
77+ @ ModifyExpressionValue (
78+ method = "method_21281" ,
79+ at = @ At (
80+ value = "INVOKE" ,
81+ target = "Lnet/minecraft/world/dimension/Dimension;method_17190(IIZ)Lnet/minecraft/util/math/BlockPos;"
82+ )
83+ )
84+ private BlockPos captureIfHasGrassBlock (
85+ BlockPos blockPos ,
86+ @ Share ("originalRandomResult" ) LocalRef <Integer > originalRandomResult ,
87+ @ Share ("validIncludingObstructed" ) LocalBooleanRef validIncludingObstructed
88+ ) {
89+ if (originalRandomResult .get () != null ) {
90+ // whether or not the spawn is obstructed, it has a grass block above sea level and is valid as an obstructed spawn if all other spawns are obstructed or invalid
91+ validIncludingObstructed .set (blockPos != null );
92+ }
93+ return blockPos ;
94+ }
95+
6796 @ ModifyVariable (method = "method_21281" , at = @ At (value = "LOAD" , ordinal = 0 ), ordinal = 5 )
68- private int fallbackOnInvalidSpawn (int p , @ Local (ordinal = 2 ) int k , @ Local (ordinal = 4 ) LocalIntRef o , @ Share ("seed" ) LocalRef <Seed > seed , @ Share ("originalRandomResult" ) LocalRef <Integer > originalRandomResult , @ Share ("newRandomValue" ) LocalRef <Integer > newRandomValue ) {
97+ private int fallbackOnInvalidSpawn (
98+ int p ,
99+ @ Local (ordinal = 2 ) int k ,
100+ @ Local (ordinal = 3 ) int n ,
101+ @ Local (ordinal = 4 ) LocalIntRef o ,
102+ @ Share ("seed" ) LocalRef <Seed > seed ,
103+ @ Share ("originalRandomResult" ) LocalRef <Integer > originalRandomResult ,
104+ @ Share ("newRandomValue" ) LocalRef <Integer > newRandomValue ,
105+ @ Share ("validIncludingObstructed" ) LocalBooleanRef validIncludingObstructed
106+ ) {
69107 // checks if the for loop is on its second iteration (p == 1), meaning the setspawn given spawn was invalid
70108 // and restores the original result of Random#nextInt
71109 if (p == 1 && originalRandomResult .get () != null ) {
@@ -77,16 +115,23 @@ private int fallbackOnInvalidSpawn(int p, @Local(ordinal = 2) int k, @Local(ordi
77115 }
78116 // if we made it to the end of the loop after an obstructed spawn and didn't find another non-obstructed spawn
79117 // redo the last iteration of the loop with the choice obstructed spawn
80- if (p == k && originalRandomResult .get () == null && newRandomValue .get () != null ) {
81- o .set (newRandomValue .get ());
118+ if (p == k && originalRandomResult .get () == null && newRandomValue .get () != null && validIncludingObstructed . get () ) {
119+ o .set (newRandomValue .get () - n * ( p - 1 ) );
82120 newRandomValue .set (null );
83121 p = k - 1 ;
84122 this .setSpawnError = null ;
85123 }
86124 return p ;
87125 }
88126
89- @ Inject (method = "method_21281" , at = @ At (value = "INVOKE" , target = "Lnet/minecraft/entity/player/ServerPlayerEntity;refreshPositionAndAngles(Lnet/minecraft/util/math/BlockPos;FF)V" , ordinal = 1 ))
127+ @ Inject (
128+ method = "method_21281" ,
129+ at = @ At (
130+ value = "INVOKE" ,
131+ target = "Lnet/minecraft/entity/player/ServerPlayerEntity;refreshPositionAndAngles(Lnet/minecraft/util/math/BlockPos;FF)V" ,
132+ ordinal = 1
133+ )
134+ )
90135 private void failOnNonRandomSpawns (CallbackInfo ci , @ Share ("seed" ) LocalRef <Seed > seed ) {
91136 if (seed .get () != null ) {
92137 this .setSpawnError = "Failed to apply SetSpawn configuration because the spawn was not random. Not overriding player spawnpoint." ;
0 commit comments