Uploaded image for project: 'Minecraft: Java Edition'
  1. Minecraft: Java Edition
  2. MC-92255

Singleplayer freezes instead of kicking a player

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Minecraft 15w45a, Minecraft 15w46a, Minecraft 15w50a, Minecraft 1.10.2, Minecraft 16w32b, Minecraft 1.11.2, Minecraft 1.12, Minecraft 1.12.1, Minecraft 1.12.2, Minecraft 1.13-pre1, Minecraft 1.13-pre3
    • Confirmed

      This is a issue which happened before 1.9 snapshots but not every time. Now in the 1.9 snapshots it seems to happen every time.

      When the player gets kicked in a singleplayer world for example for invalid movement, the client freezes instead of leaving the world properly:

      [20:27:39] [Server thread/WARN]: Marcono1234 was kicked for floating a vehicle too long!
      [20:27:39] [Server thread/INFO]: Marcono1234 lost connection: TextComponent{text='Disconnected', siblings=[], style=Style{hasParent=false, color=null, bold=null, italic=null, underlined=null, obfuscated=null, clickEvent=null, hoverEvent=null, insertion=null}}
      [20:27:39] [Server thread/INFO]: Marcono1234 left the game
      [20:27:39] [Server thread/INFO]: Stopping singleplayer server as player logged out
      [20:27:40] [Server thread/INFO]: Stopping server
      [20:27:40] [Server thread/INFO]: Saving players
      [20:27:40] [Server thread/INFO]: Saving worlds
      [20:27:40] [Server thread/INFO]: Saving chunks for level 'End Test'/Overworld
      [20:27:40] [Server thread/INFO]: Saving chunks for level 'End Test'/Nether
      [20:27:40] [Server thread/INFO]: Saving chunks for level 'End Test'/The End
      

      How to reproduce

      Use the following command (see MC-79818):

      /tellraw @p {"text":"Click me","clickEvent":{"action":"run_command","value":"\u00A7"}}
      

      The reason

      In Minecraft 1.8 (decompiled using MCP) the reason for this to happen seems to be that the initiateShutdown() method of the /Client/src/net/minecraft/server/integrated/IntegratedServer.java class is called twice. One time by the server because the hosting player left the game and the second time by the client when the main menu is loaded (or the other way around). Only the server should probably call this method.

      The following displays in which order the methods are called. However this is not correct with the timing as the server calls the initiateShutdown() method after the client called it.

      Server Client
      /Client/src/net/minecraft/network/NetHandlerPlayServer.java
      -> kickPlayerFromServer(String reason)
      /**
       * Kick a player from the server with a reason
       */
      public void kickPlayerFromServer(String reason)
      {
      	// Client side: Packets and Channel closing
      	//...
      	
      	// Server side
      	this.netManager.disableAutoRead();
      	Futures.getUnchecked(this.serverController.addScheduledTask(new Runnable()
      	{
      		private static final String __OBFID = "CL_00001454";
      		public void run()
      		{
      			NetHandlerPlayServer.this.netManager.checkDisconnected();
      		}
      	}));
      }
      
      /Client/src/net/minecraft/client/Minecraft.java
      -> runTick()
      /**
       * Runs the current tick.
       */
      public void runTick() throws IOException
      {
      	//...
      	
      	if (!this.isGamePaused && this.theWorld != null)
      	{
      		this.playerController.updateController();
      	}
      	
      	//...
      }
      
      /Client/src/net/minecraft/network/NetworkManager.java
      -> checkDisconnected()
      public void checkDisconnected()
      {
      	if (!this.hasNoChannel() && !this.isChannelOpen() && !this.disconnected)
      	{
      		this.disconnected = true;
      
      		if (this.getExitMessage() != null)
      		{
      			this.getNetHandler().onDisconnect(this.getExitMessage());
      		}
      		else if (this.getNetHandler() != null)
      		{
      			this.getNetHandler().onDisconnect(new ChatComponentText("Disconnected"));
      		}
      	}
      }
      
      /Client/src/net/minecraft/client/multiplayer/PlayerControllerMP.java
      -> updateController()
      public void updateController()
      {
      	//...
      
      	if (this.netClientHandler.getNetworkManager().isChannelOpen())
      	{
      		this.netClientHandler.getNetworkManager().processReceivedPackets();
      	}
      	else
      	{
      		this.netClientHandler.getNetworkManager().checkDisconnected();
      	}
      }
      
      /Client/src/net/minecraft/network/NetHandlerPlayServer.java
      -> onDisconnect(IChatComponent reason)
      /**
       * Invoked when disconnecting, the parameter is a ChatComponent describing the reason for termination
       */
      public void onDisconnect(IChatComponent reason)
      {
      	//...
      	
      	if (this.serverController.isSinglePlayer() && this.playerEntity.getName().equals(this.serverController.getServerOwner()))
      	{
      		logger.info("Stopping singleplayer server as player logged out");
      		this.serverController.initiateShutdown();
      	}
      }
      
      /Client/src/net/minecraft/network/NetworkManager.java
      -> checkDisconnected()
      public void checkDisconnected()
      {
      	if (!this.hasNoChannel() && !this.isChannelOpen() && !this.disconnected)
      	{
      		this.disconnected = true;
      
      		if (this.getExitMessage() != null)
      		{
      			this.getNetHandler().onDisconnect(this.getExitMessage());
      		}
      		else if (this.getNetHandler() != null)
      		{
      			this.getNetHandler().onDisconnect(new ChatComponentText("Disconnected"));
      		}
      	}
      }
      
      - /Client/src/net/minecraft/client/network/NetHandlerPlayClient.java
      -> public void onDisconnect(IChatComponent reason)
      /**
       * Invoked when disconnecting, the parameter is a ChatComponent describing the reason for termination
       */
      public void onDisconnect(IChatComponent reason)
      {
      	this.gameController.loadWorld((WorldClient)null);
      
      	//...
      }
      
      - /Client/src/net/minecraft/client/Minecraft.java
      -> loadWorld(WorldClient worldClient)
      /**
       * unloads the current world first
       */
      public void loadWorld(WorldClient worldClientIn)
      {
      	this.loadWorld(worldClientIn, "");
      }
      

      -> loadWorld(WorldClient worldClientIn, String loadingMessage)

      /**
       * par2Str is displayed on the loading screen to the user unloads the current world first
       */
      public void loadWorld(WorldClient worldClientIn, String loadingMessage)
      {
      	if (worldClientIn == null)
      	{
      		NetHandlerPlayClient var3 = this.getNetHandler();
      
      		if (var3 != null)
      		{
      			var3.cleanup();
      		}
      
      		if (this.theIntegratedServer != null && this.theIntegratedServer.func_175578_N())
      		{
      			// This should probably be only handled by the server
      			this.theIntegratedServer.initiateShutdown();
      			this.theIntegratedServer.func_175592_a();
      		}
      
      		this.theIntegratedServer = null;
      		//...
      	}
      	//...
      }
      

            cojomax99 [Mojang] Cory Scheviak
            marcono1234 Marcono1234
            Votes:
            10 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved:
              CHK: