Jump to content

[1.7.10] How to create custom spawning without modifying bed/world spawn points


McGrizzle

Recommended Posts

Hello!

 

New to Forge. I know my way around Java, so I hope I'll be able to provide enough info for anyone willing to help me. Been pulling my hair out over this for the past 2 days. :P

 

So, I've extended player properties via

IExtendedEntityProperties

and I've been attempting to control player spawning based on their properties that they have stored.

 

My mod has tile entity blocks that give players its coords on activation. These tile entities store two ints: 1 that tracks the number of spawns it has facilitated since its instantiation, and how many "resource points" it has left. When the player attempts to spawn at a tile entity they are subscribed, a resource point is deducted. If the tile entity is out of points, the player defaults to world spawn.

 

I've ran into several issues:

 

[*]When using the

EntityJoinWorldEvent

, I discovered that chunk coords are calculated before the event fires in

World.addEntityToWorld()

, making it (seemingly) impossible to accomplish my goal this way, as I can only modify the

EntityPlayer

's position, not it's chunk. Modifying the position will throw a "Wrong location!" warning

[*]The strange thing is it does in fact work, but only on first spawn. When a player first joins the world, it spawns on the tile entity block initially just fine, as its pulling the data from NBT that I set in

IExtendedEntityProperties

, but any subsequent respawn makes the player just spawn at respawn, even though I've written working persistent properties, so I know my properties are reattaching to the player just fine at respawn.

 

I'm sure the way I'm going about this is BEYOND hacky, so if anyone has any suggestion, by all means, let me know. I can provide code when I get home, but I was hoping I wouldn't have to do that, as some one might have an infinitely more elegant way of doing this. :)

Link to comment
Share on other sites

I don't know the answers for sure, but you're generally thinking the right way about the debug but in modding you also have to figure out exactly what the heck Minecraft is doing.

 

I think one issue is that Minecraft was sort of made to consider the 0, 0 (x, z) position to be the origin for generation. So it isn't quite like you generate the world and then place the player, but rather you generate the world from the player outwards. They add a bit of randomness, but only around that initial position as you can see with the

 

Anyway, it still might be possible if you catch it at the right time. I think in the worst case you can handle the player tick which I think will be late enough that you could simply set the player position.

 

Some of the things you should consider about the spawn points:

- It gets saved in WorldInfo instance, which has methods to set and get the spawnX, spawnY, and spawnZ and I think these are world positions, not just in the chunk.

- There is a getSpawnPoint() method in WorldProvider class, but I think that give point within the chunk.

- There is a setSpawnLocation() method in the WorldServer class, that that is for point within a chunk.

 

Also, in modding it is usually instructive to find something that works similar to what you want and figure out how Minecraft does it. So I'd look at beds and see how they manage changes to the spawn position.

 

But again in worst case, handle the PlayerTickEvent instead and on the first tick (use boolean flag to prevent executing the code on all subsequent ticks) move the player. There might be a slight visual glitch, but at that point in the game the chunk loading isn't that pretty anyway.

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Thanks for the reply! A lot of good stuff. +1

 

I don't know the answers for sure, but you're generally thinking the right way about the debug but in modding you also have to figure out exactly what the heck Minecraft is doing.

 

I think one issue is that Minecraft was sort of made to consider the 0, 0 (x, z) position to be the origin for generation. So it isn't quite like you generate the world and then place the player, but rather you generate the world from the player outwards. They add a bit of randomness, but only around that initial position as you can see with the

 

I spent some time with the stack trace the warning was giving me which gave me some insight.

 

[*]

World.addEntityToWorld(Entity entity)

is called after the entity is finished constructing.

[*]Vars

i

and

j

are calculated using

(entity.posX / 16.0D)

and

(entity.posZ / 16.0D)

respectively, to be used later as chunk coordinates.

[*]

EntityJoinWorldEvent

is fired, at this point allowing entity modification (I don't remember the method sig, but I know the entity gets passed)

[*]World.getChunkFromChunkCoords(i, j).addEntity(entity) is called, meaning chunk coordinates are calculated before any position editing can be done in

EntityJoinWorldEvent

, making it is impossible to modify the chunk coordinates from the event.

 

This is what made me conclude that somehow I was going about this all wrong.

 

Anyway, it still might be possible if you catch it at the right time. I think in the worst case you can handle the player tick which I think will be late enough that you could simply set the player position.

 

Some of the things you should consider about the spawn points:

- It gets saved in WorldInfo instance, which has methods to set and get the spawnX, spawnY, and spawnZ and I think these are world positions, not just in the chunk.

- There is a getSpawnPoint() method in WorldProvider class, but I think that give point within the chunk.

- There is a setSpawnLocation() method in the WorldServer class, that that is for point within a chunk.

 

Also, in modding it is usually instructive to find something that works similar to what you want and figure out how Minecraft does it. So I'd look at beds and see how they manage changes to the spawn position.

 

I was actually reviewing the Perfect Spawn mod to see how the author handled multiple spawn points. There was some mention of WorldProvider, but I otherwise didn't spend enough time with it to fully grasp it.

 

I'll look into beds, though. Thanks!

 

But again in worst case, handle the PlayerTickEvent instead and on the first tick (use boolean flag to prevent executing the code on all subsequent ticks) move the player. There might be a slight visual glitch, but at that point in the game the chunk loading isn't that pretty anyway.

 

This might be viable, from what I can tell, since my main goal is high compatibility with other mods. Any visual side-effects aren't much of a concern to me. At this point, I'm just trying to prototype, and if it's really ugly, I'll change it. Thanks for this!

 

If anyone has any other suggestions, I'd love to hear them. Otherwise, I'll post code snippets as soon as I get home so I can hopefully paint a better picture of what I'm trying to do.

Link to comment
Share on other sites

If you could provide code, it would be nice.

As to stuff - there are several events that would allow you do thing you are doing. Problem is that Minecraft won't (not full clean).

ServerConfigurationManager#recreatePlayerEntity - recreates EntityPlayer after death, there are also set all positions bed spawns, everything.

Sadly - there are no hooks that are launched before server sends data packets about new EntityPlayer (look code).

Your best shot is to go with:

@SubscribeEvent
public void onPlayerRespawn(PlayerEvent.PlayerRespawnEvent event)
{
	event.player.setPositionAndUpdate(100, 100, 100); // And use values from IEEP.
}

This event is launched from FML only on server, after reconstrucing and entity joining world - you are safe to use IEEP and any kind of new data.

 

This is for respawn ofc., For normal spawning (joining) you can use bunch of other events:

PlayerEvent.PlayerLoggedInEvent

PlayerEvent.PlayerChangedDimensionEvent

etc.

 

Disclaimer - I might have totally missed the point of what are you doing. I was more or less basing on thread title.

 

 

EDIT

Btw. if you really would like to go hardcore - you can make "nice" trick with data-swapping.

Bed-spawn position is accessible and changeable - the moment of player's death you can check if he should use your respawn system, if yes, copy position from your IEEP and swap bed-spawn loaction with it, that way the vanilla code will actually do everything on it's own, but using your swapped data. Then, again in PlayerRespawnEvent, you can swap data back to make it "normal".

 

This will save you bunch of (internal) packets and fix the problem with loading unnecessary chunks.

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

Thanks guys for all the help. I was able to get something up and running!

 

EDIT

Btw. if you really would like to go hardcore - you can make "nice" trick with data-swapping.

Bed-spawn position is accessible and changeable - the moment of player's death you can check if he should use your respawn system, if yes, copy position from your IEEP and swap bed-spawn loaction with it, that way the vanilla code will actually do everything on it's own, but using your swapped data. Then, again in PlayerRespawnEvent, you can swap data back to make it "normal".

 

This will save you bunch of (internal) packets and fix the problem with loading unnecessary chunks.

 

This was the first thing I tried, but I kept getting invalid spawn point warnings in chat about a lack of a bed within the chunk, even though I was passing the

forced

flag. I gave up after moving it between different events still without luck.

 

But again in worst case, handle the PlayerTickEvent instead and on the first tick (use boolean flag to prevent executing the code on all subsequent ticks) move the player. There might be a slight visual glitch, but at that point in the game the chunk loading isn't that pretty anyway.

 

This did it! It's not particularly elegant, but it works as expected! Can definitely use this for prototyping, plus the visual shenanigans are negligible, even when moving great distances.

 

Here's my code, in case you were curious.

 

My tick handler:

public class PlayerTickEventHandler {

static boolean isFirstTick;

public PlayerTickEventHandler() {
	this.isFirstTick = true;
}

@SubscribeEvent
public void onPlayerTick(PlayerTickEvent event) {
	if (isFirstTick) {
		if (SupplyPlayer.get(event.player).hasSpawn){
			ChatUtils.sendServerMsg("Tick hit!");
			event.player.setPositionAndUpdate(SupplyPlayer.get(event.player).supplyX, 
                                                                                         SupplyPlayer.get(event.player).supplyY, 
								                 SupplyPlayer.get(event.player).supplyZ);
		}
	this.isFirstTick = false;
	}
}
}

 

My spawn event handler (I rolled the two events into a single class, but I'm not sure if this is best practice):

public class PlayerSpawnEventsHandler {

PlayerSpawnEventsHandler(){};

@SubscribeEvent 
public void onEntityJoinWorldEvent(EntityJoinWorldEvent event) {
	if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer) {
		PlayerTickEventHandler.isFirstTick = true;
		NBTTagCompound playerData = CommonProxy.getPlayerNBT(((EntityPlayer) event.entity).getDisplayName());
		if (playerData != null) {
			SupplyPlayer.getFromEvent(event).loadNBTData(playerData);
		}

	}
}

@SubscribeEvent
public void onLivingDeathEvent(LivingDeathEvent event) {
	if (!event.entity.worldObj.isRemote && event.entity instanceof EntityPlayer) {
		ChatUtils.sendServerMsg("Player successfully detected on death!");
		NBTTagCompound playerData = new NBTTagCompound();
		SupplyPlayer.getFromEvent(event).saveNBTData(playerData);
		CommonProxy.storePlayerNBT(((EntityPlayer) event.entity).getDisplayName(), playerData);
	}
}
}

 

And finally, my IEEP class (pretty much entirely lifted from coolAlias's awesome IEEP tutorial):

public class SupplyPlayer implements IExtendedEntityProperties {

public final static String EXT_PROP_NAME = "SupplyPlayer";

private EntityPlayer player;
public double supplyX, supplyY, supplyZ;
public boolean hasSpawn;

public SupplyPlayer(EntityPlayer player) {
	this.player = player;
	this.supplyX = 0;
	this.supplyY = 0;
	this.supplyZ = 0;
	this.hasSpawn = false;
}

public static void register(EntityPlayer player) {
	player.registerExtendedProperties(EXT_PROP_NAME, new SupplyPlayer(player));
}

public static SupplyPlayer get(EntityPlayer player) {
	return (SupplyPlayer) player.getExtendedProperties(EXT_PROP_NAME);
}

public static SupplyPlayer getFromEvent(EntityEvent event) {
	return SupplyPlayer.get((EntityPlayer) event.entity);
}

private static String getSaveKey(EntityPlayer player) {
	return player.getDisplayName() + ":" + EXT_PROP_NAME;
}

public static void saveProxyData(EntityPlayer player) {
	SupplyPlayer playerData = SupplyPlayer.get(player);

	NBTTagCompound savedData = new NBTTagCompound();

	playerData.saveNBTData(savedData);

	CommonProxy.storePlayerNBT(getSaveKey(player), savedData);
}

public static void loadProxyData(EntityPlayer player) {
	SupplyPlayer playerData = SupplyPlayer.get(player);
	NBTTagCompound savedData = CommonProxy.getPlayerNBT(getSaveKey(player));

	if(savedData != null) {
		playerData.loadNBTData(savedData);
	}
}

public void updateCoords(int supplyX, int supplyY, int supplyZ) {
	this.supplyX = supplyX;
	this.supplyY = supplyY;
	this.supplyZ = supplyZ;
	this.hasSpawn = true;
}

@Override
public void saveNBTData(NBTTagCompound compound) {
	NBTTagCompound props = new NBTTagCompound();

	props.setDouble("supplyX", this.supplyX);
	props.setDouble("supplyY", this.supplyY);
	props.setDouble("supplyZ", this.supplyZ);
	props.setBoolean("hasSpawn", this.hasSpawn);

	compound.setTag(EXT_PROP_NAME, props);
}


// Loads NBT data
@Override
public void loadNBTData(NBTTagCompound compound) {
	NBTTagCompound props = (NBTTagCompound) compound.getTag(EXT_PROP_NAME);

	this.supplyX = props.getDouble("supplyX");
	this.supplyY = props.getDouble("supplyY");
	this.supplyZ = props.getDouble("supplyZ");
	this.hasSpawn = props.getBoolean("hasSpawn");
}


// Basically useless for now
@Override
public void init(Entity entity, World world) {
}
}

 

Thanks again for the help. Hugely appreciated! Btw, if either of you have suggestions regarding my code, let me know. It's unfinished and I'm prone to making mistakes, but I'm also not the best programmer in the world, so I'm bound to overlook things.

Link to comment
Share on other sites

Cool. Your code looks good to me, you have similar coding style. Regarding whether your event handling methods are in single class or separate classes, I personally throw them all into one class per event bus. Then I don't have to do the extra step of registering the class on the bus each time I decide to add some event handling. But it there is nothing wrong with having them separate.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Err, about that 'awesome' tutorial, the part about persisting data across death by storing in the proxy is outdated - I made a post about a better method here, but haven't gotten around to updating the actual thread due to the 'new and improved and almost completely unusable' forum editor on MCF.

 

The gist of it is subscribe to PlayerEvent.Clone and use the previous player instance to copy the old IEEP data to the new player instance. Way cleaner than the old method.

Link to comment
Share on other sites

Err, about that 'awesome' tutorial, the part about persisting data across death by storing in the proxy is outdated - I made a post about a better method here, but haven't gotten around to updating the actual thread due to the 'new and improved and almost completely unusable' forum editor on MCF.

 

The gist of it is subscribe to PlayerEvent.Clone and use the previous player instance to copy the old IEEP data to the new player instance. Way cleaner than the old method.

 

Thanks for popping in! If that's the case, I'll probably update it when I migrate everything to 1.8. I began this mod with the intention of making it compatible with another mod, but the mod in question is no longer maintained, and at this point, I'm mainly making a proof-of-concept. I'll add this to the list though, since so far, testing has gone smoothly, and everything is starting to get a little messy.

 

But hey, it was awesome to me. It got the results I wanted, and premature optimization is the devil's right hand. :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • The mobs are moving normally for a second, then freeze, then go back to normal, and this repeats. I describe this as mob lag, bc they stutter as if they were lagging. I don't have the best wifi, but that shouldn't matter since its 10mbps which is enough for a server. I have 8 gigs of ram for it, no optimizations have worked, if there is anything more you may need to help lmk, ill describe. The modlist is pretty hefty: VS_ClockworkSurvivalSupportV1.4 aether-1.20.1-1.2.0-neoforge.jar alexsdelight-1.5.jar alexsmobs-1.22.7.jar appleskin-forge-mc1.20.1-2.5.1.jar backpacked-forge-1.20.1-2.2.5.jar balm-forge-1.20.1-7.2.1.jar bettervillage-forge-1.20.1-3.2.0.jar BiomesOPlenty-1.20.1-18.0.0.598.jar botarium-forge-1.20.1-2.3.2.jar CGM-Unofficial-1.4.11+Forge+1.20.1.jar citadel-2.5.2-1.20.1.jar clockwork-1.20.1-0.1.2-forge-05f5d5fbbb.jar configured-forge-1.20.1-2.2.2.jar connectivity-1.20.1-4.9.jar create-1.20.1-0.5.1.f.jar create-new-age-forge-1.20.1-1.1.2.jar createarmoryv0.6.1n.jar createbigcannons-forge-1.20.1-0.5.3.b.jar createchunkloading-1.6.0-forge.jar createdeco-2.0.1-1.20.1-forge.jar createdieselgenerators-1.20.1-1.2h.jar createendertransmission-2.0.7-1.20.1.jar create_misc_and_things_ 1.20.1_4.0A.jar cristellib-1.1.5-forge.jar cupboard-1.20.1-2.3.jar curios-forge-5.7.0+1.20.1.jar destroy-1.20.1-0.8.jar Dungeon Crawl-1.20.1-2.3.14.jar FallingTree-1.20.1-4.3.4.jar FarmersDelight-1.20.1-1.2.4.jar framework-forge-1.20.1-0.6.26.jar geckolib-forge-1.20.1-4.4.2.jar InsaneLib-1.12.1-mc1.20.1.jar Jade-1.20.1-forge-11.8.0.jar jei-1.20.1-forge-15.3.0.4.jar journeymap-1.20.1-5.9.18-forge.jar JustEnoughResources-1.20.1-1.4.0.247.jar kotlinforforge-4.10.0-all.jar l2library-2.4.23.jar libraryferret-forge-1.20.1-4.0.0.jar macawsbridgesbop-1.20-1.1.jar mcw-bridges-2.1.0-mc1.20.4forge.jar moonlight-1.20-2.9.17-forge.jar morevillagers-forge-1.20.1-5.0.0.jar Mo_ZombiesWave-1.20.1-0.1.0-forge.jar NaturesCompass-1.20.1-1.11.2-forge.jar Placebo-1.20.1-8.6.1.jar refinedstorage-1.12.4.jar soundphysics-forge-1.20.1-1.1.2.jar spark-1.10.53-forge.jar stealing_villagers-forge-1.20.1-1.0.0.jar s_a_b-1.3.0_1.20.1.jar takesapillage-1.0.3-1.20.1.jar TerraBlender-forge-1.20.1-3.0.1.4.jar tombstone-1.20.1-8.6.2.jar Towns-and-Towers-1.12-Fabric+Forge.jar twilightforest-1.20.1-4.3.2145-universal.jar valkyrienskies-120-2.1.3-beta.1.jar veinmining-forge-1.3.0+1.20.1.jar waystones-forge-1.20-14.1.3.jar YungsApi-1.20-Forge-4.0.3.jar YungsBetterDesertTemples-1.20-Forge-3.0.3.jar YungsBetterDungeons-1.20-Forge-4.0.3.jar YungsBetterEndIsland-1.20-Forge-2.0.5.jar YungsBetterJungleTemples-1.20-Forge-2.0.4.jar YungsBetterMineshafts-1.20-Forge-4.0.4.jar YungsBetterNetherFortresses-1.20-Forge-2.0.5.jar YungsBetterOceanMonuments-1.20-Forge-3.0.4.jar YungsBetterStrongholds-1.20-Forge-4.0.3.jar YungsBetterWitchHuts-1.20-Forge-3.0.3.jar YungsBridges-1.20-Forge-4.0.3.jar zaynens_leather_from_rotten_flesh_mod_1.20.1.jar
    • Thank you so much! Unfortunately before reading this I reinstalled my java installer. Now when I try to open the forge installer I receive an error message stating 'Unable to install Java' even though I have definitely downloaded and installed the latest version. Any idea what this could be or how I could fix it?  
    • IT WORKED! THANK YOU!
    • sorry for the stuborn ness ss   test code logs and planks return nothing on minable but i can use the other tags  name minecraft:oak_log BlockTags.LOGS name minecraft:oak_planks BlockTags.PLANKS   it kinda works but its not like like perfect             
    • I'm making a custom modpack for 1.20.1 and everything else seems to work except this Passive skilltree mod. The world loads fine but when I press the keybind to open the mod's GUI, the world immediately saves and crashes with this report: https://pastebin.com/svEPsRty Any help or insight towards the right direction is greatly appreciated!
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.