Jump to content

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


Recommended Posts

Posted

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. :)

Posted

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/

Posted

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

 

  On 4/20/2015 at 3:03 PM, jabelar said:

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.

 

  Quote

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!

 

  Quote

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.

Posted

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.

  Quote

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

Posted

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

 

  On 4/20/2015 at 7:17 PM, Ernio said:

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.

 

  Quote

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.

Posted

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/

Posted

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.

Posted
  On 4/22/2015 at 4:08 AM, coolAlias said:

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. :)

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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • After some time minecraft crashes with an error. Here is the log https://drive.google.com/file/d/1o-2R6KZaC8sxjtLaw5qj0A-GkG_SuoB5/view?usp=sharing
    • The specific issue is that items in my inventory wont stack properly. For instance, if I punch a tree down to collect wood, the first block I collected goes to my hand. So when I punch the second block of wood to collect it, it drops, but instead of stacking with the piece of wood already in my hand, it goes to the second slot in my hotbar instead. Another example is that I'll get some dirt, and then when I'm placing it down later I'll accidentally place a block where I don't want it. When I harvest it again, it doesn't go back to the stack that it came from on my hotbar, where it should have gone, but rather into my inventory. That means that if my inventory is full, then the dirt wont be picked up even though there should be space available in the stack I'm holding. The forge version I'm using is 40.3.0, for java 1.18.2. I'll leave the mods I'm using here, and I'd appreciate it if anybody can point me in the right direction in regards to figuring out how to fix this. I forgot to mention that I think it only happens on my server but I'm not entirely sure. PLEASE HELP ME! LIST OF THE MODS. aaa_particles Adorn AdvancementPlaques AI-Improvements AkashicTome alexsdelight alexsmobs AmbientSounds amwplushies Animalistic another_furniture AppleSkin Aquaculture aquamirae architectury artifacts Atlas-Lib AutoLeveling AutoRegLib auudio balm betterfpsdist biggerstacks biomancy BiomesOPlenty blockui blueprint Bookshelf born_in_chaos Botania braincell BrassAmberBattleTowers brutalbosses camera CasinoCraft cfm (MrCrayfish’s Furniture Mod) chat_heads citadel cloth-config Clumps CMDCam CNB cobweb collective comforts convenientcurioscontainer cookingforblockheads coroutil CosmeticArmorReworked CozyHome CrabbersDelight crashexploitfixer crashutilities Create CreativeCore creeperoverhaul cristellib crittersandcompanions Croptopia CroptopiaAdditions CullLessLeaves curios curiouslanterns curiouslights Curses' Naturals CustomNPCs CyclopsCore dannys_expansion decocraft Decoration Mod DecorationDelightRefurbished Decorative Blocks Disenchanting DistantHorizons doubledoors DramaticDoors drippyloadingscreen durabilitytooltip dynamic-fps dynamiclights DynamicTrees DynamicTreesBOP DynamicTreesPlus Easy Dungeons EasyAnvils EasyMagic easy_npc eatinganimation ecologics effective_fg elevatorid embeddium emotecraft enchantlimiter EnchantmentDescriptions EnderMail engineersdecor entityculling entity_model_features entity_texture_features epicfight EvilCraft exlinefurniture expandability explosiveenhancement factory-blocks fairylights fancymenu FancyVideo FarmersDelight fast-ip-ping FastSuite ferritecore finsandtails FixMySpawnR Forge Middle Ages fossil FpsReducer2 furnish GamingDeco geckolib goblintraders goldenfood goodall H.e.b habitat harvest-with-ease hexerei hole_filler huge-structure-blocks HunterIllager iammusicplayer Iceberg illuminations immersive_paintings incubation infinitybuttons inventoryhud InventoryProfilesNext invocore ItemBorders itemzoom Jade jei (Just Enough Items) JetAndEliasArmors journeymap JRFTL justzoom kiwiboi Kobolds konkrete kotlinforforge lazydfu LegendaryTooltips libIPN lightspeed lmft lodestone LongNbtKiller LuckPerms Lucky77 MagmaMonsters malum ManyIdeasCore ManyIdeasDoors marbledsarsenal marg mcw-furniture mcw-lights mcw-paths mcw-stairs mcw-trapdoors mcw-windows meetyourfight melody memoryleakfix Mimic minecraft-comes-alive MineTraps minibosses MmmMmmMmmMmm MOAdecor (ART, BATH, COOKERY, GARDEN, HOLIDAYS, LIGHTS, SCIENCE) MobCatcher modonomicon mods_optimizer morehitboxes mowziesmobs MutantMonsters mysticalworld naturalist NaturesAura neapolitan NekosEnchantedBooks neoncraft2 nerb nifty NightConfigFixes nightlights nocube's_villagers_sell_animals NoSeeNoTick notenoughanimations obscure_api oculus oresabovediamonds otyacraftengine Paraglider Patchouli physics-mod Pillagers Gun PizzaCraft placeableitems Placebo player-animation-lib pneumaticcraft-repressurized polymorph PrettyPipes Prism projectbrazier Psychadelic-Chemistry PuzzlesLib realmrpg_imps_and_demons RecipesLibrary reeves-furniture RegionsUnexplored restrictedportals revive-me Scary_Mobs_And_Bosses selene shetiphiancore ShoulderSurfing smoothboot
    • Hi everyone, I'm currently developing a Forge 1.21 mod for Minecraft and I want to display a custom HUD overlay for a minigame. My goal: When the game starts, all players should see an item/block icon (from the base game, not a custom texture) plus its name/text in the HUD – similar to how the bossbar overlay works. The HUD should appear centered above the hotbar (or at a similar prominent spot), and update dynamically (icon and name change as the target item changes). What I've tried: I looked at many online tutorials and several GitHub repos (e.g. SeasonHUD, MiniHUD), but most of them use NeoForge or Forge versions <1.20 that provide the IGuiOverlay API (e.g. implements IGuiOverlay, RegisterGuiOverlaysEvent). In Forge 1.21, it seems that neither IGuiOverlay nor RegisterGuiOverlaysEvent exist anymore – at least, I can't import them and they are missing from the docs and code completion. I tried using RenderLevelStageEvent as a workaround but it is probably not intended for custom HUDs. I am not using NeoForge, and switching the project to NeoForge is currently not an option for me. I tried to look at the original minecraft source code to see how elements like hearts, hotbar etc are drawn on the screen but I am too new to Minecraft modding to understand. What I'm looking for: What is the correct way to add a custom HUD element (icon + text) in Forge 1.21, given that the previous overlay API is missing? Is there a new recommended event, callback, or method in Forge 1.21 for custom HUD overlays, or is everyone just using a workaround? Is there a minimal open-source example repo for Forge 1.21 that demonstrates a working HUD overlay without relying on NeoForge or deprecated Forge APIs? My ideal solution: Centered HUD element with an in-game item/block icon (from the base game's assets, e.g. a diamond or any ItemStack / Item) and its name as text, with a transparent background rectangle. It should be visible to the players when the mini game is running. Easy to update the item (e.g. static variable or other method), so it can change dynamically during the game. Any help, code snippets, or up-to-date references would be really appreciated! If this is simply not possible right now in Forge 1.21, it would also help to know that for sure. Thank you very much in advance!
    • The simple answer is there is not an easy way. You would need to know how to program in Java, as well as at least some familiarity with how Forge works so you could port the differences. You would also need the sourcecode for the original mod, and permission from the author to modify it, if they did not use some sort of open source license. So it's not impossible, but it would take some effort, but doing so would open up a whole new world of possibilities for you!
  • Topics

×
×
  • Create New...

Important Information

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