Jump to content

[1.7.10+] Keeping player (or his clone) after he logs out.


Recommended Posts

Basically ani-logout system. I want that if ExtendedPlayer.get(player).canLogOut() is false, player will be locked for let's say 10sec after he disconnects.

I mean: client can disconnect in any way - exiting game or closing it, but server will spawn some fake player and keep it for 10sec.


Critical points: I have 3 ideas:


- How to not-remove EntityPlayer from entity list?

- If i could do this, whole problem becomes very easy to resolve.


2. (If I can't keep real player, like in 1.)

- Should I spawn new EntityPlayer and clone() data from old one via PlayerLoggedOutEvent and operate on new entity.

- In this case - since they are clons, will this give me ability to edit "real" player's .dat file? (I mean, when I remove new clonned EntityPlayer, will it call saveNBT() like "real" one would - keeping the player/session UUID)?

- If I remove clonned EntityPlayer - will it call PlayerLoggedOutEvent and make finite loop (until player#canLogOut()), OR PlayerLoggedOutEvent is only called on disconnect?


3. Use some kind of Abstract/Fake Player, I heard Forge has some class with this name, I have no idea what's the use of it.


Okay so, please post you knowledge/ideas/critical-points. :)


Thanks all :P

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

Link to comment
Share on other sites

Dude, there is NO "better" way to do this. I need EntityPlayer to be IN WORLD and be treaten like you never logged out (killable, movable) after session is ended. That is literally what it means to have "ani-logout system", you can't run from combat, can't logout if threatened, can't logout with poison on you (to escape some player that want to loot you).


I'd like straight from-experience answers to this thread, or idea other than mine, or why mine wouldn't work. Thanks for help :P

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

Link to comment
Share on other sites

Maybe you can try to catch the


, check if the


and then use


with the a button overriding the


method to first check if you can log out, and if so, proceed, else cancel.

Don't PM me with questions. They will be ignored! Make a thread on the appropriate board for support.


1.12 -> 1.13 primer by williewillus.


1.7.10 and older versions of Minecraft are no longer supported due to it's age! Update to the latest version for support.



Link to comment
Share on other sites

That's Client-side, I need server-side solution.


You can close game without pressing "Quit Game" button. If you do that client will never send disconnect info, therefore player will still be logged on server and server will throw UnexpectedConnection error and kick player after short while. But that's not what I want. I need player to presist as long as I need him too (not too long ofc, few, maybe dozen of seconds).


To post above - he posed to right topic (i mean, hopefully), just from different point of view (client). :)

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

Link to comment
Share on other sites

Here is how to do it in 1.7.2, you will have to leverage changes from 1.7.10


Setup event for player logout and record the players name and inventory.


If you are having trouble getting the inventory before logout, something like this may help.



In the below example skin is the players name


        // Get NBT data

            NBTTagCompound nbt = instance.player_manager().player_nbt(skin);


            // Check for null

            if (nbt != null) {


            // Get Inventory tag

            NBTTagList nbttaglist = nbt.getTagList("Inventory", 10);


            // Read into it







Spawn a Custom NPC with the skin (name) of the player.

- you need a datawatcher to move the skin name over to the client side

- Give the npc the inventory, weapons ect from what you saved above

- on the custom render for the npc look up the skin, you can look up skin with this



// Get the skin location

texture = new ResourceLocation("skins/" + StringUtils.stripControlCodes(name));

// Download the skin

ThreadDownloadImageData skin = getDownloadImageSkin(texture, name);




Put a timer in the NPC logic for how long you want it to exist.


Watch for if someone or something kills it.  If so, erase the players inventory on the server.  To find the players data you can adapt some code I had to move the player out of a deleted dimension.



public void offlinePlayerDimension_Check() {


        // Setup Variables

        String base = "[Player_Manager][offlinePlayerDimension_Check] : ";

        SaveHandler saveHandler = (SaveHandler) DimensionManager.getWorld(0).getSaveHandler();


        // Create high level Directory

        File player_directory = new File(saveHandler.getWorldDirectory(), "players");


        // Cycle through stored players

        String[] players = saveHandler.getAvailablePlayerDat();


        for (String item : players) {


            // Get the NBT Data

            NBTTagCompound tag = saveHandler.getPlayerData(item);


            // Grab the current dimension

            int current_dimension = tag.getInteger("Dimension");


            // Grab the home world

            World world = instance.functions_common().find_world(0);


            // If the Dimension does not exists, move the player

            if (!instance.functions_common().dimension_check(current_dimension)) {


                // Set to safe dimension

                tag.setInteger("Dimension", 0);


                // Set to spawn

                tag.setTag("Pos", this.newDoubleNBTList(new double[] {world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnY() + 1.0d, world.getWorldInfo().getSpawnZ()}));


                // Try and save the data

                try {


                    // Make a temporary file

                    File file1 = new File(player_directory, item + ".dat.tmp");


                    // Grab the old file

                    File file2 = new File(player_directory, item + ".dat");


                    // Write to the new file

                    CompressedStreamTools.writeCompressed(tag, new FileOutputStream(file1));


                    // See if the file exists and remove it

                    if (file2.exists()) {






                    // Move the file name over



                    // notify the system

                    instance.logger.warn(base + "Moved player " + item + " from non-Dimension " + current_dimension);


                } catch (Exception e) {


                    // notify the system

                    instance.logger.warn(base + "Failed to mov player " + item + " from non-Dimension " + current_dimension);











Long time Bukkit & Forge Programmer

Happy to try and help

Link to comment
Share on other sites


"You won't get around making a new entity most likely. In that entity..."

So either I missunderstood or you ment "will" not "won't", doesn't matter - I'll probably get around faking player anyway.



Problem here is not with loading/saving even not copying data and keeping it. Main thing I am worried is one you didn't place in you post - how to fake EntityPlayer. Ofc thanks for your post :)


Spawning an EntityPlayer and putting data from EntityPlayer (clonning) that logged out brought me questions about how it will be handled by game. EntityPlayer is quite an unique object (one per session) which left me with questions about what happens to stuff like playerUUIDs (which is held not-in EntityPlayer) or playerIP.


In the end I'll probably just get it done like in "idea 2." and see what happens. If I can't save that data I'll probably use some caching that will be read on rejoin like diesieben suggested.


Any further knowledge ofc always appreciated. :)

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

Link to comment
Share on other sites

If by Custom NPC you mean EntityPlayer then yeah, that's what I'll do.


"Im pretty sure the mod Sync uses this aswell"

Love you, checking out Repo right now :)


I'll just experiment a bit and post solutions/questions later :)


Thanks all.



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

Link to comment
Share on other sites

By Custom_NPC, i meant Custom_NPC.


I've never like the result of making a fake player.  I've found it much easier to create an NPC that will act like a player, stick a nametag above it, and copy the inventory over. 

Long time Bukkit & Forge Programmer

Happy to try and help

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.

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 version of what? I just imported the MDK into Eclipse. I haven't made any edits to the Java code in it or even any other files. I haven't even gotten to changing the modid from "examplemod" yet.
    • Captain WebGenesis is a Cyber skilled hacker with special abilities and skills that enact penetration to help victims of Crypto Fraudulent activities who are faced with cyber challenges get back their lost funds. Simply file your complaint through the website (https://captainwebgenesis. com) and have all your crypto recovered back.
    • The crash log keeps saying something about the tick rate. Can someone please help me fix?   ---- Minecraft Crash Report ---- // Everything's going to plan. No, really, that was supposed to happen. Time: 2024-06-21 16:24:55 Description: Exception in server tick loop java.lang.NullPointerException: Cannot invoke "com.cobblemon.mod.common.api.conditional.RegistryLikeCondition.fits(Object, net.minecraft.core.Registry)" because "condition" is null at com.cobblemon.mod.common.api.spawning.condition.SpawningCondition.fits(SpawningCondition.java:85) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.condition.AreaTypeSpawningCondition.fits(AreaTypeSpawningCondition.java:30) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.condition.GroundedTypeSpawningCondition.fits(GroundedTypeSpawningCondition.java:28) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.condition.GroundedTypeSpawningCondition.fits(GroundedTypeSpawningCondition.java:24) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.condition.SpawningCondition.isSatisfiedBy(SpawningCondition.java:68) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.detail.SpawnDetail.isSatisfiedBy(SpawnDetail.java:78) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.spawner.Spawner$DefaultImpls.getMatchingSpawns(Spawner.java:42) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner.getMatchingSpawns(TickingSpawner.java:31) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector.getSelectionData(FlatContextWeightedSelector.java:79) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.selection.FlatContextWeightedSelector.select(FlatContextWeightedSelector.java:106) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.spawner.AreaSpawner.run(AreaSpawner.java:94) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner.tick(TickingSpawner.java:71) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.spawning.SpawnerManager.onServerTick(SpawnerManager.java:58) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.events.ServerTickHandler.onTick(ServerTickHandler.java:20) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.Cobblemon$initialize$7.invoke(Cobblemon.java:368) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.Cobblemon$initialize$7.invoke(Cobblemon.java:368) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.reactive.ObservableSubscription.handle(ObservableSubscription.java:16) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.common.api.reactive.SimpleObservable.emit(SimpleObservable.java:39) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.forge.event.ForgePlatformEventHandler.onTick(ForgePlatformEventHandler.kt:172) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading} at com.cobblemon.mod.forge.event.__ForgePlatformEventHandler_onTick_ServerTickEvent.invoke(.dynamic) ~[Cobblemon-forge-1.5.2+1.20.1.jar%23121!/:?] {re:classloading,pl:eventbus:B} at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:73) ~[eventbus-6.0.5.jar%2352!/:?] {} at net.minecraftforge.eventbus.EventBus.post(EventBus.java:315) ~[eventbus-6.0.5.jar%2352!/:?] {} at net.minecraftforge.eventbus.EventBus.post(EventBus.java:296) ~[eventbus-6.0.5.jar%2352!/:?] {} at net.minecraftforge.event.ForgeEventFactory.onPostServerTick(ForgeEventFactory.java:950) ~[forge-1.20.1-47.2.17-universal.jar%23138!/:?] {re:classloading} at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:835) ~[server-1.20.1-20230612.114412-srg.jar%23133!/:?] {re:computing_frames,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:661) ~[server-1.20.1-20230612.114412-srg.jar%23133!/:?] {re:computing_frames,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.m_206580_(MinecraftServer.java:251) ~[server-1.20.1-20230612.114412-srg.jar%23133!/:?] {re:computing_frames,pl:accesstransformer:B,re:classloading,pl:accesstransformer:B,re:mixin,pl:accesstransformer:B} at java.lang.Thread.run(Thread.java:840) ~[?:?] {re:mixin}
    • okay i've managed to fix it. looks like i made the stupidest error known to man. i hadn't installed forge to the client, which i assumed only affected the launcher which wouldn't affect me because i used a third party one. evidently not. the more you know
    • tried a fresh install of above and beyond with java 8 installed. got a popup that says "Error: Unable to access jarfile forge-1.16.5-36.2.8.jar". every good programmer knows getting a different error means you've made progress!
  • Topics

  • Create New...

Important Information

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