internal bool JoinWorldNow( [NotNull] World newWorld, bool doUseWorldSpawn, WorldChangeReason reason )
{
if ( newWorld == null )
throw new ArgumentNullException( "newWorld" );
if ( !Enum.IsDefined( typeof( WorldChangeReason ), reason ) ) {
throw new ArgumentOutOfRangeException( "reason" );
}
if ( Thread.CurrentThread != ioThread ) {
throw new InvalidOperationException( "Player.JoinWorldNow may only be called from player's own thread. " +
"Use Player.JoinWorld instead." );
}
string textLine1 = ConfigKey.ServerName.GetString();
string textLine2;
if ( IsUsingWoM && ConfigKey.WoMEnableEnvExtensions.Enabled() ) {
if ( IP.Equals( IPAddress.Loopback ) ) {
textLine2 = "cfg=localhost:" + Server.Port + "/" + newWorld.Name;
} else {
textLine2 = "cfg=" + Server.ExternalIP + ":" + Server.Port + "/" + newWorld.Name;
}
} else {
textLine2 = "Loading world " + newWorld.ClassyName;
}
if ( RaisePlayerJoiningWorldEvent( this, newWorld, reason, textLine1, textLine2 ) ) {
Logger.Log( LogType.Warning,
"Player.JoinWorldNow: Player {0} was prevented from joining world {1} by an event callback.",
Name, newWorld.Name );
return false;
}
World oldWorld = World;
// remove player from the old world
if ( oldWorld != null && oldWorld != newWorld ) {
if ( !oldWorld.ReleasePlayer( this ) ) {
Logger.Log( LogType.Error,
"Player.JoinWorldNow: Player asked to be released from its world, " +
"but the world did not contain the player." );
}
}
ResetVisibleEntities();
ClearLowPriotityOutputQueue();
Map map;
// try to join the new world
if ( oldWorld != newWorld ) {
bool announce = ( oldWorld != null ) && ( oldWorld.Name != newWorld.Name );
map = newWorld.AcceptPlayer( this, announce );
if ( map == null ) {
return false;
}
} else {
map = newWorld.LoadMap();
}
World = newWorld;
// Set spawn point
if ( doUseWorldSpawn ) {
Position = map.Spawn;
} else {
Position = postJoinPosition;
}
// Start sending over the level copy
if ( oldWorld != null ) {
SendNow( PacketWriter.MakeHandshake( this, textLine1, textLine2 ) );
}
writer.WriteMapBegin();
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 mapStream = new MemoryStream() ) {
map.GetCompressedCopy( mapStream, true );
blockData = mapStream.ToArray();
}
Logger.Log( LogType.Debug,
"Player.JoinWorldNow: Sending compressed map ({0} bytes) to {1}.",
blockData.Length, 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.WriteMapChunk( buffer, chunkSize, progress );
BytesSent += 1028;
mapBytesSent += chunkSize;
}
// Turn off Nagel's algorithm again for LowLatencyMode
client.NoDelay = ConfigKey.LowLatencyMode.Enabled();
// Done sending over level copy
writer.WriteMapEnd( map );
BytesSent += 7;
// Sets player's spawn point to map spawn
writer.WriteAddEntity( 255, this, map.Spawn );
BytesSent += 74;
// Teleport player to the target location
// This allows preserving spawn rotation/look, and allows
// teleporting player to a specific location (e.g. /TP or /Bring)
writer.WriteTeleport( 255, Position );
BytesSent += 10;
if ( oldWorld == newWorld ) {
if ( !newWorld.IsRealm ) {
Message( "Rejoined world {0}", newWorld.ClassyName );
} else {
Message( "Rejoined realm {0}", newWorld.ClassyName );
}
} else {
if ( !newWorld.IsRealm ) {
Message( "Joined world {0}", newWorld.ClassyName );
} else {
Message( "Joined realm {0}", newWorld.ClassyName );
}
if ( newWorld != WorldManager.MainWorld ) {
newWorld.VisitCount++;
}
string greeting = newWorld.Greeting;
if ( greeting != null ) {
greeting = Chat.ReplaceTextKeywords( this, greeting );
Message( "&R* {0}: {1}", newWorld.Name, greeting );
}
}
RaisePlayerJoinedWorldEvent( this, oldWorld, reason );
// Done.
Server.RequestGC();
return true;
}