Merge branch 'multisub'

Conflicts:
	.gitignore
	Subsurface/Source/Characters/Animation/HumanoidAnimController.cs
	Subsurface/Source/Items/Components/Holdable/RepairTool.cs
	Subsurface_Solution.v12.suo
This commit is contained in:
Regalis
2016-07-04 17:42:31 +03:00
87 changed files with 2496 additions and 750 deletions

View File

@@ -413,6 +413,11 @@ namespace Barotrauma.Networking
reliableChannel.Update(deltaTime);
if (gameStarted && respawnManager != null)
{
respawnManager.Update(deltaTime);
}
if (updateTimer > DateTime.Now) return;
if (myCharacter != null)
@@ -518,6 +523,9 @@ namespace Barotrauma.Networking
string endMessage = inc.ReadString();
CoroutineManager.StartCoroutine(EndGame(endMessage));
break;
case (byte)PacketTypes.Respawn:
if (gameStarted && respawnManager != null) respawnManager.ReadNetworkEvent(inc);
break;
case (byte)PacketTypes.PlayerJoined:
Client otherClient = new Client(inc.ReadString(), inc.ReadByte());
@@ -672,7 +680,9 @@ namespace Barotrauma.Networking
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, Mission.MissionTypes[missionTypeIndex]);
GameMain.GameSession.StartShift(levelSeed);
respawnManager = new RespawnManager(this);
yield return CoroutineStatus.Running;
//myCharacter = ReadCharacterData(inc);
@@ -725,11 +735,13 @@ namespace Barotrauma.Networking
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
respawnManager = null;
float endPreviewLength = 10.0f;
if (Screen.Selected == GameMain.GameScreen)
{
var cinematic = new TransitionCinematic(Submarine.Loaded, GameMain.GameScreen.Cam, endPreviewLength);
var cinematic = new TransitionCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, endPreviewLength);
float secondsLeft = endPreviewLength;
@@ -774,11 +786,6 @@ namespace Barotrauma.Networking
if (fileStreamReceiver != null &&
(fileStreamReceiver.Status == FileTransferStatus.Receiving || fileStreamReceiver.Status == FileTransferStatus.NotStarted))
{
//Vector2 pos = Screen.Selected == GameMain.NetLobbyScreen ?
// new Vector2(GameMain.NetLobbyScreen.SubList.Rect.X, GameMain.NetLobbyScreen.SubList.Rect.Bottom+5) : new Vector2(GameMain.GraphicsWidth / 2 - 200, 10);
Vector2 pos = new Vector2(GameMain.GraphicsWidth / 2 - 130, GameMain.NetLobbyScreen.InfoFrame.Rect.Y / 2 - 15);
GUI.DrawString(spriteBatch,
@@ -799,6 +806,16 @@ namespace Barotrauma.Networking
}
}
if (respawnManager != null && respawnManager.CurrentState == RespawnManager.State.Waiting &&
myCharacter != null && myCharacter.IsDead)
{
GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth - 300.0f, 20),
"Respawning in " + (int)respawnManager.RespawnTimer + " s",
Color.White, null, 0, GUI.SmallFont);
}
if (!GameMain.DebugDraw) return;
int width = 200, height = 300;
@@ -1023,7 +1040,7 @@ namespace Barotrauma.Networking
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
private Character ReadCharacterData(NetIncomingMessage inc, bool isMyCharacter)
public Character ReadCharacterData(NetIncomingMessage inc, bool isMyCharacter)
{
string newName = inc.ReadString();
ushort ID = inc.ReadUInt16();

View File

@@ -282,6 +282,8 @@ namespace Barotrauma.Networking
{
inGameHUD.Update((float)Physics.step);
respawnManager.Update(deltaTime);
bool isCrewDead =
connectedClients.Find(c => c.Character != null && !c.Character.IsDead)==null &&
(myCharacter == null || myCharacter.IsDead);
@@ -289,7 +291,7 @@ namespace Barotrauma.Networking
//restart if all characters are dead or submarine is at the end of the level
if ((autoRestart && isCrewDead)
||
(endRoundAtLevelEnd && Submarine.Loaded!=null && Submarine.Loaded.AtEndPosition))
(endRoundAtLevelEnd && Submarine.MainSub != null && Submarine.MainSub.AtEndPosition))
{
if (AutoRestart && isCrewDead)
{
@@ -363,9 +365,10 @@ namespace Barotrauma.Networking
{
if (!(c is AICharacter) || c.IsDead) continue;
Vector2 diff = c.WorldPosition-Submarine.Loaded.WorldPosition;
//todo: take multiple subs into account
//Vector2 diff = c.WorldPosition - Submarine.MainSub.WorldPosition;
if (FarseerPhysics.ConvertUnits.ToSimUnits(diff.Length()) > NetConfig.CharacterIgnoreDistance) continue;
//if (FarseerPhysics.ConvertUnits.ToSimUnits(diff.Length()) > NetConfig.CharacterIgnoreDistance) continue;
new NetworkEvent(NetworkEventType.EntityUpdate, c.ID, false);
}
@@ -389,7 +392,13 @@ namespace Barotrauma.Networking
private void SparseUpdate()
{
if (gameStarted) new NetworkEvent(Submarine.Loaded.ID, false);
if (gameStarted)
{
foreach (Submarine sub in Submarine.Loaded)
{
new NetworkEvent(sub.ID, false);
}
}
foreach (Character c in Character.CharacterList)
{
@@ -397,9 +406,10 @@ namespace Barotrauma.Networking
if (c is AICharacter)
{
Vector2 diff = c.WorldPosition - Submarine.Loaded.WorldPosition;
//todo: take multiple subs into account
//Vector2 diff = c.WorldPosition - Submarine.MainSub.WorldPosition;
if (FarseerPhysics.ConvertUnits.ToSimUnits(diff.Length()) > NetConfig.CharacterIgnoreDistance) continue;
//if (FarseerPhysics.ConvertUnits.ToSimUnits(diff.Length()) > NetConfig.CharacterIgnoreDistance) continue;
}
new NetworkEvent(NetworkEventType.ImportantEntityUpdate, c.ID, false);
@@ -601,7 +611,7 @@ namespace Barotrauma.Networking
case (byte)PacketTypes.SpectateRequest:
if (gameStarted && allowSpectating)
{
var startMessage = CreateStartMessage(roundStartSeed, Submarine.Loaded, GameMain.GameSession.gameMode.Preset);
var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset);
server.SendMessage(startMessage, inc.SenderConnection, NetDeliveryMethod.ReliableUnordered);
dataSender.Spectating = true;
@@ -840,6 +850,10 @@ namespace Barotrauma.Networking
//save "normal" events again
existingEvents = new List<NetworkEvent>(NetworkEvent.Events);
}
yield return new WaitForSeconds(0.1f);
sender.inGame = true;
yield return CoroutineStatus.Success;
}
@@ -941,6 +955,8 @@ namespace Barotrauma.Networking
GameServer.Log("Game mode: " + selectedMode.Name, Color.Cyan);
GameServer.Log("Level seed: " + GameMain.NetLobbyScreen.LevelSeed, Color.Cyan);
respawnManager = new RespawnManager(this);
yield return CoroutineStatus.Running;
List<CharacterInfo> characterInfos = new List<CharacterInfo>();
@@ -964,7 +980,7 @@ namespace Barotrauma.Networking
characterInfos.Add(characterInfo);
}
WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos);
WayPoint[] assignedWayPoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub);
for (int i = 0; i < connectedClients.Count; i++)
{
@@ -985,7 +1001,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.CrewManager.characters.Add(myCharacter);
}
var startMessage = CreateStartMessage(roundStartSeed, Submarine.Loaded, GameMain.GameSession.gameMode.Preset);
var startMessage = CreateStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset);
SendMessage(startMessage, NetDeliveryMethod.ReliableUnordered);
@@ -1090,6 +1106,8 @@ namespace Barotrauma.Networking
GameMain.GameScreen.Cam.TargetPos = Vector2.Zero;
GameMain.LightManager.LosEnabled = false;
respawnManager = null;
gameStarted = false;
if (connectedClients.Count > 0)
@@ -1113,7 +1131,7 @@ namespace Barotrauma.Networking
float endPreviewLength = 10.0f;
var cinematic = new TransitionCinematic(Submarine.Loaded, GameMain.GameScreen.Cam, endPreviewLength);
var cinematic = new TransitionCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, endPreviewLength);
float secondsLeft = endPreviewLength;
@@ -1132,6 +1150,14 @@ namespace Barotrauma.Networking
}
public void SendRespawnManagerMsg()
{
NetOutgoingMessage msg = server.CreateMessage();
respawnManager.WriteNetworkEvent(msg);
SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}
private void DisconnectClient(NetConnection senderConnection, string msg = "", string targetmsg = "")
{
Client client = connectedClients.Find(x => x.Connection == senderConnection);
@@ -1322,6 +1348,14 @@ namespace Barotrauma.Networking
log.LogFrame.Draw(spriteBatch);
}
if (respawnManager != null && respawnManager.CurrentState == RespawnManager.State.Waiting && respawnManager.CountdownStarted)
{
GUI.DrawString(spriteBatch,
new Vector2(GameMain.GraphicsWidth - 500.0f, 20),
"Respawning in " + (int)respawnManager.RespawnTimer + " s",
Color.White, null, 0, GUI.SmallFont);
}
if (!ShowNetStats) return;
int width = 200, height = 300;
@@ -1603,7 +1637,7 @@ namespace Barotrauma.Networking
}
}
private void WriteCharacterData(NetOutgoingMessage message, string name, Character character)
public void WriteCharacterData(NetOutgoingMessage message, string name, Character character)
{
message.Write(name);
message.Write(character.ID);

View File

@@ -39,7 +39,9 @@ namespace Barotrauma.Networking
RequestFile, FileStream,
SpectateRequest
SpectateRequest,
Respawn
}
enum VoteType
@@ -74,6 +76,9 @@ namespace Barotrauma.Networking
protected Character myCharacter;
protected CharacterInfo characterInfo;
protected RespawnManager respawnManager;
public Voting Voting;
public Character Character

View File

@@ -0,0 +1,312 @@
using Barotrauma.Items.Components;
using FarseerPhysics;
using Lidgren.Network;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Barotrauma.Networking
{
class RespawnManager
{
const int MinCharactersToRespawn = 1;
const float RespawnInterval = 20.0f;
public enum State
{
Waiting,
Transporting,
Returning
}
private NetworkMember networkMember;
private State state;
private Submarine respawnShuttle;
private Steering shuttleSteering;
private List<Door> shuttleDoors;
public float RespawnTimer
{
get { return respawnTimer; }
}
public bool CountdownStarted
{
get;
private set;
}
public State CurrentState
{
get { return state; }
}
private float respawnTimer, shuttleReturnTimer;
public RespawnManager(NetworkMember server)
{
this.networkMember = server;
respawnShuttle = new Submarine("Submarines/Shuttle Mark I.sub");
respawnShuttle.Load(false);
ResetShuttlePos();
respawnShuttle.GodMode = true;
shuttleDoors = new List<Door>();
foreach (Item item in Item.ItemList)
{
if (item.Submarine != respawnShuttle) continue;
var steering = item.GetComponent<Steering>();
if (steering != null) shuttleSteering = steering;
var door = item.GetComponent<Door>();
if (door != null) shuttleDoors.Add(door);
}
shuttleSteering.TargetPosition = ConvertUnits.ToSimUnits(Level.Loaded.StartPosition);
respawnTimer = RespawnInterval;
}
private List<Client> GetClientsToRespawn()
{
return networkMember.ConnectedClients.FindAll(c => c.inGame && (c.Character == null || c.Character.IsDead));
}
public void Update(float deltaTime)
{
switch (state)
{
case State.Waiting:
UpdateWaiting(deltaTime);
break;
case State.Transporting:
UpdateTransporting(deltaTime);
break;
case State.Returning:
UpdateReturning(deltaTime);
break;
}
}
private void UpdateWaiting(float deltaTime)
{
var server = networkMember as GameServer;
if (server == null)
{
if (CountdownStarted)
{
respawnTimer = Math.Max(0.0f, respawnTimer - deltaTime);
}
return;
}
respawnShuttle.Velocity = Vector2.Zero;
shuttleSteering.AutoPilot = false;
shuttleSteering.MaintainPos = false;
int characterToRespawnCount = GetClientsToRespawn().Count;
if (server.Character != null && server.Character.IsDead) characterToRespawnCount++;
CountdownStarted = characterToRespawnCount >= MinCharactersToRespawn;
if (!CountdownStarted) return;
respawnTimer -= deltaTime;
if (respawnTimer <= 0.0f)
{
respawnTimer = RespawnInterval;
Respawn();
}
}
private void UpdateTransporting(float deltaTime)
{
var server = networkMember as GameServer;
if (server == null) return;
if (Character.CharacterList.Any(c => c.Submarine == respawnShuttle && !c.IsDead)) return;
shuttleReturnTimer += deltaTime;
if (shuttleReturnTimer > 10.0f)
{
state = State.Returning;
server.SendRespawnManagerMsg();
shuttleReturnTimer = 0.0f;
}
}
private void UpdateReturning(float deltaTime)
{
shuttleReturnTimer += deltaTime;
if (shuttleReturnTimer > 1.0f)
{
shuttleSteering.AutoPilot = true;
shuttleSteering.MaintainPos = false;
foreach (Door door in shuttleDoors)
{
if (door.IsOpen) door.SetState(false, false, true);
}
var server = networkMember as GameServer;
if (server == null) return;
//shuttle has returned if the path has been traversed or the shuttle is close enough to the exit
if (shuttleSteering.SteeringPath != null && shuttleSteering.SteeringPath.Finished
|| (respawnShuttle.WorldPosition.Y + respawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f))
{
CoroutineManager.StartCoroutine(
ForceShuttleToPos(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + 1000.0f), 100.0f));
if (GameMain.GameSession != null && GameMain.GameSession.Map != null)
{
string msg = "The transportation shuttle has returned to " + GameMain.GameSession.Map.SelectedLocation;
server.SendChatMessage(ChatMessage.Create("", msg, ChatMessageType.Server, null), server.ConnectedClients);
}
state = State.Waiting;
server.SendRespawnManagerMsg();
}
shuttleReturnTimer = 0.0f;
}
}
private void Respawn()
{
var server = networkMember as GameServer;
if (server == null) return;
state = State.Transporting;
ResetShuttlePos();
server.SendChatMessage(ChatMessage.Create("", "Transportation shuttle dispatched", ChatMessageType.Server, null), server.ConnectedClients);
server.SendRespawnManagerMsg();
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f));
}
private IEnumerable<object> ForceShuttleToPos(Vector2 position, float speed)
{
respawnShuttle.SubBody.Body.IgnoreCollisionWith(Level.Loaded.ShaftBodies[0]);
while (Math.Abs(position.Y - respawnShuttle.WorldPosition.Y) > 100.0f)
{
Vector2 displayVel = Vector2.Normalize(position - respawnShuttle.WorldPosition) * speed;
respawnShuttle.SubBody.Body.LinearVelocity = ConvertUnits.ToSimUnits(displayVel);
yield return CoroutineStatus.Running;
if (respawnShuttle.SubBody == null) yield return CoroutineStatus.Success;
}
respawnShuttle.SubBody.Body.RestoreCollisionWith(Level.Loaded.ShaftBodies[0]);
yield return CoroutineStatus.Success;
}
private void ResetShuttlePos()
{
respawnShuttle.SetPosition(new Vector2(Level.Loaded.StartPosition.X, Level.Loaded.Size.Y + respawnShuttle.Borders.Height));
respawnShuttle.Velocity = Vector2.Zero;
}
public void WriteNetworkEvent(NetOutgoingMessage msg)
{
var server = networkMember as GameServer;
msg.Write((byte)PacketTypes.Respawn);
msg.WriteRangedInteger(0, Enum.GetNames(typeof(State)).Length, (int)state);
switch (state)
{
case State.Transporting:
var clients = GetClientsToRespawn();
List<CharacterInfo> characterInfos = clients.Select(c => c.characterInfo).ToList();
if (server.Character != null && server.Character.IsDead) characterInfos.Add(server.CharacterInfo);
var waypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, respawnShuttle);
msg.Write((byte)characterInfos.Count);
for (int i = 0; i < characterInfos.Count; i++)
{
var character = Character.Create(characterInfos[i], waypoints[i].WorldPosition, true, false);
if (i < clients.Count)
{
msg.Write((byte)clients[i].ID);
}
else
{
msg.Write((byte)0);
server.Character = character;
Character.Controlled = character;
}
character.GiveJobItems(waypoints[i]);
GameMain.GameSession.CrewManager.characters.Add(character);
server.WriteCharacterData(msg, character.Name, character);
}
break;
case State.Waiting:
msg.Write(respawnTimer);
break;
}
}
public void ReadNetworkEvent(NetIncomingMessage inc)
{
state = (State)inc.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
switch (state)
{
case State.Transporting:
CountdownStarted = false;
ResetShuttlePos();
var client = networkMember as GameClient;
int clientCount = inc.ReadByte();
for (int i = 0; i < clientCount; i++)
{
byte clientId = inc.ReadByte();
client.ReadCharacterData(inc, clientId == client.ID);
}
CoroutineManager.StartCoroutine(ForceShuttleToPos(Level.Loaded.StartPosition - Vector2.UnitY * Level.ShaftHeight, 100.0f));
break;
case State.Waiting:
CountdownStarted = true;
respawnTimer = inc.ReadSingle();
break;
case State.Returning:
CountdownStarted = false;
break;
}
}
}
}