public bool JoinWorld( World newWorld, bool useHandshakePacket ) { if( newWorld.classAccess.rank > player.info.playerClass.rank ) { return false; } if( !newWorld.FirePlayerTriedToJoinEvent( player ) ) { return false; } isBetweenWorlds = true; if( player.world != null ) { player.world.ReleasePlayer( player ); } ClearQueues(); client.NoDelay = false; World oldWorld = player.world; newWorld.AcceptPlayer( player ); player.world = newWorld; // Start sending over the level copy if( useHandshakePacket ) { writer.Write( PacketWriter.MakeHandshake( player, Config.GetString( ConfigKey.ServerName ), "Loading world \"" + player.world.name + "\"" ) ); } writer.WriteLevelBegin(); byte[] buffer = new byte[1024]; int bytesSent = 0; // Fetch compressed map copy byte[] blockData; using( MemoryStream stream = new MemoryStream() ) { player.world.map.GetCompressedCopy( stream, true ); blockData = stream.ToArray(); } Logger.Log( "Session.LoginSequence: Sending compressed level copy ({0} bytes) to {1}.", LogType.Debug, blockData.Length, player.name ); while ( bytesSent < blockData.Length ) { int chunkSize = blockData.Length - bytesSent; if ( chunkSize > 1024 ) { chunkSize = 1024; } Array.Copy( blockData, bytesSent, buffer, 0, chunkSize ); byte progress = (byte)( 100 * bytesSent / blockData.Length ); // write in chunks of 1024 bytes or less writer.WriteLevelChunk( buffer, chunkSize, progress ); bytesSent += chunkSize; } writer.Write( PacketWriter.MakeHandshake( player, Config.GetString( ConfigKey.ServerName ), "Almost there..." ) ); // Done sending over level copy writer.Write( PacketWriter.MakeLevelEnd( player.world.map ) ); // Send new spawn player.pos = player.world.map.spawn; Thread.Sleep( 100 ); writer.WriteAddEntity( 255, player, player.pos ); writer.WriteTeleport( 255, player.pos ); isBetweenWorlds = false; // Send player list player.world.SendPlayerList( player ); if( Config.GetBool( ConfigKey.LowLatencyMode ) ) { client.NoDelay = true; } Server.FireWorldChangedEvent( player, oldWorld, newWorld ); // Done. GC.Collect( GC.MaxGeneration, GCCollectionMode.Optimized ); return true; }
internal bool JoinWorldNow( World newWorld, bool firstTime, bool doUseWorldSpawn ) { if( newWorld == null ) throw new ArgumentNullException( "newWorld" ); if( !Player.CanJoin( newWorld ) ) { Logger.Log( "Session.JoinWorldNow: Access limits prevented {0} from joining {1}.", LogType.Error, Player.Name, newWorld.Name ); return false; } if( !newWorld.FirePlayerTriedToJoinEvent( Player ) ) { Logger.Log( "Session.JoinWorldNow: FirePlayerTriedToJoinEvent prevented {0} from joining {1}", LogType.Warning, Player.Name, newWorld.Name ); return false; } World oldWorld = Player.World; // remove player from the old world if( oldWorld != null && oldWorld != newWorld ) { if( !oldWorld.ReleasePlayer( Player ) ) { Logger.Log( "Session.JoinWorldNow: Player asked to be released from its world, " + "but the world did not contain the player.", LogType.Error ); } } ResetVisibleEntities(); ClearBlockUpdateQueue(); Map map; // try to join the new world if( oldWorld != newWorld ) { bool announce = !firstTime && (oldWorld.Name != newWorld.Name); map = newWorld.AcceptPlayer( Player, announce ); if( map == null ) { return false; } } else { map = oldWorld.EnsureMapLoaded(); } Player.World = newWorld; // Set spawn point Position spawn; if( doUseWorldSpawn ) { spawn = map.Spawn; }else{ spawn = postJoinPosition; } Player.Position = spawn; // Start sending over the level copy if( !firstTime ) { SendNow( PacketWriter.MakeHandshake( Player, ConfigKey.ServerName.GetString(), "Loading world " + newWorld.GetClassyName() ) ); } writer.WriteLevelBegin(); bytesSent++; // enable Nagle's algorithm (in case it was turned off by LowLatencyMode) // to avoid wasting bandwidth for map transfer client.NoDelay = false; // Fetch compressed map copy byte[] buffer = new byte[1024]; int mapBytesSent = 0; byte[] blockData; using( MemoryStream stream = new MemoryStream() ) { map.GetCompressedCopy( stream, true ); blockData = stream.ToArray(); } Logger.Log( "Session.JoinWorldNow: Sending compressed level copy ({0} bytes) to {1}.", LogType.Debug, blockData.Length, Player.Name ); // Transfer the map copy while( mapBytesSent < blockData.Length ) { int chunkSize = blockData.Length - mapBytesSent; if( chunkSize > 1024 ) { chunkSize = 1024; } else { // CRC fix for ManicDigger for( int i = 0; i < buffer.Length; i++ ) { buffer[i] = 0; } } Array.Copy( blockData, mapBytesSent, buffer, 0, chunkSize ); byte progress = (byte)(100 * mapBytesSent / blockData.Length); // write in chunks of 1024 bytes or less writer.WriteLevelChunk( buffer, chunkSize, progress ); bytesSent += 1028; mapBytesSent += chunkSize; } // Done sending over level copy writer.WriteLevelEnd( map ); bytesSent += 7; // Send spawn point writer.WriteAddEntity( 255, Player, map.Spawn ); bytesSent += 74; writer.WriteTeleport( 255, spawn ); bytesSent += 10; Player.Message( "Joined world {0}", newWorld.GetClassyName() ); // Turn off Nagel's algorithm again for LowLatencyMode if( ConfigKey.LowLatencyMode.GetBool() ) { client.NoDelay = true; } Server.FireWorldChangedEvent( Player, oldWorld, newWorld ); // Done. if( MonoCompat.IsMono ) { Server.RequestGC(); } return true; }