3 using System.Collections.Generic;
10 [AddComponentMenu(
"")]
13 private const float JitterTimeFactor = 0.001f;
15 private const string serverSettingsAssetFile =
"TrueSyncGlobalConfig";
17 private enum StartState { BEHAVIOR_INITIALIZED, FIRST_UPDATE, STARTED };
19 private StartState startState;
30 if (_TrueSyncGlobalConfig == null) {
34 return _TrueSyncGlobalConfig;
45 private AbstractLockstep lockstep;
47 private FP lockedTimeStep;
52 private List<TrueSyncManagedBehaviour> generalBehaviours;
57 private Dictionary<byte, List<TrueSyncManagedBehaviour>> behaviorsByPlayer;
62 private CoroutineScheduler scheduler;
67 private List<TrueSyncManagedBehaviour> queuedBehaviours =
new List<TrueSyncManagedBehaviour>();
69 private Dictionary<ITrueSyncBehaviour, TrueSyncManagedBehaviour> mapBehaviorToManagedBehavior =
new Dictionary<ITrueSyncBehaviour, TrueSyncManagedBehaviour>();
76 if (instance == null) {
80 return instance.lockstep.deltaTime;
89 if (instance == null || instance.lockstep == null) {
93 return instance.lockstep.time;
102 if (instance == null || instance.lockstep == null) {
106 return instance.lockstep.Ticks;
115 if (instance == null || instance.lockstep == null) {
119 return instance.lockstep.LastSafeTick;
128 if (instance == null) {
141 if (instance == null || instance.lockstep == null) {
145 List<TSPlayerInfo> allPlayers =
new List<TSPlayerInfo>();
146 foreach (TSPlayer tsp
in instance.lockstep.Players.Values) {
148 allPlayers.Add(tsp.playerInfo);
161 if (instance == null || instance.lockstep == null) {
165 return instance.lockstep.LocalPlayer.playerInfo;
174 if (instance == null) {
178 return instance.ActiveConfig;
186 if (TrueSyncCustomConfig != null) {
187 customConfig = TrueSyncCustomConfig;
188 TrueSyncCustomConfig = null;
191 if (customConfig != null) {
195 return TrueSyncGlobalConfig;
214 Application.runInBackground =
true;
216 if (ReplayRecord.replayMode == ReplayMode.LOAD_REPLAY) {
217 ReplayRecord replayRecord = ReplayRecord.replayToLoad;
218 if (replayRecord == null) {
219 Debug.LogError(
"Replay Record can't be loaded");
220 gameObject.SetActive(
false);
226 if (!PhotonNetwork.connected || !PhotonNetwork.inRoom) {
227 Debug.LogWarning(
"You are not connected to Photon. TrueSync will start in offline mode.");
234 lockstep = AbstractLockstep.NewInstance(
245 OnPlayerDisconnection,
251 this.gameObject.AddComponent<TrueSyncStats>().Lockstep = lockstep;
254 scheduler =
new CoroutineScheduler(lockstep);
256 if (ReplayRecord.replayMode != ReplayMode.LOAD_REPLAY) {
257 if (communicator == null) {
258 lockstep.AddPlayer(0,
"Local_Player",
true);
260 List<PhotonPlayer> players =
new List<PhotonPlayer>(PhotonNetwork.playerList);
263 foreach (PhotonPlayer p
in players) {
264 lockstep.AddPlayer((byte)p.ID, p.name, p.isLocal);
269 generalBehaviours =
new List<TrueSyncManagedBehaviour>();
271 generalBehaviours.Add(NewManagedBehavior(tsb));
275 initGeneralBehaviors(generalBehaviours,
false);
279 startState = StartState.BEHAVIOR_INITIALIZED;
282 private TrueSyncManagedBehaviour NewManagedBehavior(ITrueSyncBehaviour trueSyncBehavior) {
283 TrueSyncManagedBehaviour result =
new TrueSyncManagedBehaviour(trueSyncBehavior);
284 mapBehaviorToManagedBehavior[trueSyncBehavior] = result;
289 private void initBehaviors() {
290 behaviorsByPlayer =
new Dictionary<byte, List<TrueSyncManagedBehaviour>>();
292 foreach (TSPlayer p
in lockstep.Players.Values) {
293 List<TrueSyncManagedBehaviour> behaviorsInstatiated =
new List<TrueSyncManagedBehaviour>();
295 foreach (GameObject prefab
in playerPrefabs) {
296 GameObject prefabInst = Instantiate(prefab);
297 InitializeGameObject(prefabInst, prefabInst.transform.position.ToTSVector(), prefabInst.transform.rotation.ToTSQuaternion());
301 behaviour.
owner = p.playerInfo;
302 behaviour.
localOwner = lockstep.LocalPlayer.playerInfo;
305 behaviorsInstatiated.Add(NewManagedBehavior(behaviour));
309 behaviorsByPlayer.Add(p.ID, behaviorsInstatiated);
313 private void initGeneralBehaviors(IEnumerable<TrueSyncManagedBehaviour> behaviours,
bool realOwnerId) {
314 List<TSPlayer> playersList =
new List<TSPlayer>(lockstep.Players.Values);
315 List<TrueSyncManagedBehaviour> itemsToRemove =
new List<TrueSyncManagedBehaviour>();
317 foreach (TrueSyncManagedBehaviour tsmb
in behaviours) {
322 TrueSyncBehaviour bh = (TrueSyncBehaviour)tsmb.trueSyncBehavior;
332 if (behaviorsByPlayer.ContainsKey((byte)bh.
ownerIndex)) {
335 behaviorsByPlayer[(byte)bh.
ownerIndex].Add(tsmb);
336 itemsToRemove.Add(tsmb);
341 bh.
localOwner = lockstep.LocalPlayer.playerInfo;
345 foreach (TrueSyncManagedBehaviour bh
in itemsToRemove) {
346 generalBehaviours.Remove(bh);
350 private void CheckQueuedBehaviours() {
351 if (queuedBehaviours.Count > 0) {
352 generalBehaviours.AddRange(queuedBehaviours);
353 initGeneralBehaviors(queuedBehaviours,
true);
355 foreach (TrueSyncManagedBehaviour tsmb
in queuedBehaviours) {
356 tsmb.SetGameInfo(lockstep.LocalPlayer.playerInfo, lockstep.Players.Count);
357 tsmb.OnSyncedStart();
360 queuedBehaviours.Clear();
365 if (lockstep != null && startState != StartState.STARTED) {
366 if (startState == StartState.BEHAVIOR_INITIALIZED) {
367 startState = StartState.FIRST_UPDATE;
368 }
else if (startState == StartState.FIRST_UPDATE) {
369 lockstep.RunSimulation(
true);
370 startState = StartState.STARTED;
379 if (instance != null && instance.lockstep != null) {
380 instance.lockstep.RunSimulation(
false);
388 if (instance != null && instance.lockstep != null) {
389 instance.lockstep.PauseSimulation();
397 if (instance != null && instance.lockstep != null) {
398 instance.lockstep.EndSimulation();
406 if (instance != null && instance.lockstep != null) {
407 instance.scheduler.UpdateAllCoroutines();
417 if (instance != null && instance.lockstep != null) {
418 instance.scheduler.StartCoroutine(coroutine);
428 return SyncedInstantiate(prefab, prefab.transform.position.ToTSVector(), prefab.transform.rotation.ToTSQuaternion());
439 if (instance != null && instance.lockstep != null && (prefab.GetComponentInChildren<
TSTransform>() != null || prefab.GetComponentInChildren<
TSTransform2D>() != null)) {
440 GameObject go = GameObject.Instantiate(prefab, position.ToVector(), rotation.ToQuaternion()) as GameObject;
442 foreach (MonoBehaviour bh
in go.GetComponentsInChildren<MonoBehaviour>()) {
443 if (bh is ITrueSyncBehaviour) {
444 instance.queuedBehaviours.Add(instance.NewManagedBehavior((ITrueSyncBehaviour)bh));
448 InitializeGameObject(go, position, rotation);
456 private static void InitializeGameObject(GameObject go,
TSVector position,
TSQuaternion rotation) {
457 ICollider[] tsColliders = go.GetComponentsInChildren<ICollider>();
458 if (tsColliders != null) {
459 foreach (ICollider tsCollider
in tsColliders) {
465 if (rootTSTransform != null) {
468 rootTSTransform.
position = position;
469 rootTSTransform.
rotation = rotation;
473 if (tsTransforms != null) {
474 foreach (
TSTransform tsTransform
in tsTransforms) {
475 if (tsTransform != rootTSTransform) {
482 if (rootTSTransform2D != null) {
485 rootTSTransform2D.
position =
new TSVector2(position.
x, position.
y);
486 rootTSTransform2D.
rotation = rotation.ToQuaternion().eulerAngles.
z;
490 if (tsTransforms2D != null) {
492 if (tsTransform2D != rootTSTransform2D) {
518 if (instance != null && instance.lockstep != null) {
522 if (tsColliders != null) {
523 foreach (
TSCollider tsCollider
in tsColliders) {
524 DestroyTSRigidBody(tsCollider.gameObject, tsCollider.
Body);
529 if (tsColliders2D != null) {
531 DestroyTSRigidBody(tsCollider2D.gameObject, tsCollider2D.
Body);
541 foreach (MonoBehaviour tsb
in gameObject.GetComponentsInChildren<MonoBehaviour>()) {
542 if (tsb is ITrueSyncBehaviour && instance.mapBehaviorToManagedBehavior.ContainsKey((ITrueSyncBehaviour)tsb)) {
543 instance.mapBehaviorToManagedBehavior[(ITrueSyncBehaviour)tsb].disabled =
true;
553 private static void DestroyTSRigidBody(GameObject tsColliderGO,
IBody body) {
554 tsColliderGO.gameObject.SetActive(
false);
555 instance.lockstep.Destroy(body);
564 if (instance != null && instance.lockstep != null) {
565 instance.queuedBehaviours.Add(instance.NewManagedBehavior(trueSyncBehaviour));
575 if (instance != null && instance.lockstep != null) {
576 instance.lockstep.GameIsReady += IsReadyChecker;
586 if (instance != null && instance.lockstep != null) {
587 foreach (TrueSyncManagedBehaviour tsmb
in instance.behaviorsByPlayer[(byte)playerId]) {
588 tsmb.disabled =
true;
591 if (tsColliders != null) {
592 foreach (
TSCollider tsCollider
in tsColliders) {
594 DestroyTSRigidBody(tsCollider.gameObject, tsCollider.
Body);
600 if (tsCollider2Ds != null) {
603 DestroyTSRigidBody(tsCollider2D.gameObject, tsCollider2D.
Body);
611 private FP tsDeltaTime = 0;
614 if (lockstep != null) {
617 if (tsDeltaTime >= (lockedTimeStep - JitterTimeFactor)) {
620 instance.scheduler.UpdateAllCoroutines();
626 void GetLocalData(InputData playerInputData) {
627 TrueSyncInput.CurrentInputData = playerInputData;
629 if (behaviorsByPlayer.ContainsKey(playerInputData.ownerID)) {
630 foreach (TrueSyncManagedBehaviour bh
in behaviorsByPlayer[playerInputData.ownerID]) {
631 if (bh != null && !bh.disabled) {
637 TrueSyncInput.CurrentInputData = null;
640 void OnStepUpdate(InputData[] allInputData) {
641 TrueSyncInput.GetAllInputs().Clear();
643 if (generalBehaviours != null) {
644 foreach (TrueSyncManagedBehaviour bh
in generalBehaviours) {
645 if (bh != null && !bh.disabled) {
646 bh.OnPreSyncedUpdate();
647 instance.scheduler.UpdateAllCoroutines();
652 foreach (InputData playerInputData
in allInputData) {
653 if (behaviorsByPlayer.ContainsKey(playerInputData.ownerID)) {
654 foreach (TrueSyncManagedBehaviour bh
in behaviorsByPlayer[playerInputData.ownerID]) {
655 if (bh != null && !bh.disabled) {
656 bh.OnPreSyncedUpdate();
657 instance.scheduler.UpdateAllCoroutines();
663 TrueSyncInput.GetAllInputs().AddRange(allInputData);
665 TrueSyncInput.CurrentSimulationData = null;
666 if (generalBehaviours != null) {
667 foreach (TrueSyncManagedBehaviour bh
in generalBehaviours) {
668 if (bh != null && !bh.disabled) {
670 instance.scheduler.UpdateAllCoroutines();
675 foreach (InputData playerInputData
in allInputData) {
676 if (behaviorsByPlayer.ContainsKey(playerInputData.ownerID)) {
677 TrueSyncInput.CurrentSimulationData = playerInputData;
679 foreach (TrueSyncManagedBehaviour bh
in behaviorsByPlayer[playerInputData.ownerID]) {
680 if (bh != null && !bh.disabled) {
682 instance.scheduler.UpdateAllCoroutines();
687 TrueSyncInput.CurrentSimulationData = null;
690 CheckQueuedBehaviours();
693 private void OnRemovedRigidBody(
IBody body) {
697 List<TrueSyncBehaviour> behavioursToRemove =
new List<TrueSyncBehaviour>(go.GetComponentsInChildren<
TrueSyncBehaviour>());
698 RemoveFromTSMBList(queuedBehaviours, behavioursToRemove);
699 RemoveFromTSMBList(generalBehaviours, behavioursToRemove);
701 foreach (List<TrueSyncManagedBehaviour> listBh
in behaviorsByPlayer.Values) {
702 RemoveFromTSMBList(listBh, behavioursToRemove);
707 private void RemoveFromTSMBList(List<TrueSyncManagedBehaviour> tsmbList, List<TrueSyncBehaviour> behaviours) {
708 List<TrueSyncManagedBehaviour> toRemove =
new List<TrueSyncManagedBehaviour>();
709 foreach (TrueSyncManagedBehaviour tsmb
in tsmbList) {
710 if ((tsmb.trueSyncBehavior is
TrueSyncBehaviour) && behaviours.Contains((TrueSyncBehaviour)tsmb.trueSyncBehavior)) {
715 foreach (TrueSyncManagedBehaviour tsmb
in toRemove) {
716 tsmbList.Remove(tsmb);
720 void OnPlayerDisconnection(byte playerId) {
721 GenericOnGameCall(
"TrueSyncManagedBehaviour.OnPlayerDisconnection",
new object[] { (int)playerId });
724 void OnGameStarted() {
725 GenericOnGameCall(
"TrueSyncManagedBehaviour.OnSyncedStart");
726 CheckQueuedBehaviours();
729 void OnGamePaused() {
730 GenericOnGameCall(
"TrueSyncManagedBehaviour.OnGamePaused");
733 void OnGameUnPaused() {
734 GenericOnGameCall(
"TrueSyncManagedBehaviour.OnGameUnPaused");
738 GenericOnGameCall(
"TrueSyncManagedBehaviour.OnGameEnded");
741 void GenericOnGameCall(
string callbackName) {
742 GenericOnGameCall(callbackName, null);
745 void GenericOnGameCall(
string callbackName,
object[] parameter) {
746 if (generalBehaviours != null) {
747 foreach (TrueSyncManagedBehaviour bh
in generalBehaviours) {
749 instance.scheduler.UpdateAllCoroutines();
753 foreach (List<TrueSyncManagedBehaviour> behaviors
in behaviorsByPlayer.Values) {
754 foreach (TrueSyncManagedBehaviour bh
in behaviors) {
756 instance.scheduler.UpdateAllCoroutines();
761 void OnApplicationQuit() {
int rollbackWindow
Rollback window size.
Manages creation of player prefabs and lockstep execution.
FP y
The Y component of the vector.
TSPlayerInfo owner
Basic info about the owner of this behaviour.
TSPlayerInfo localOwner
Basic info about the local player.
static void UpdateCoroutines()
Update all coroutines created.
static void RegisterITrueSyncBehaviour(ITrueSyncBehaviour trueSyncBehaviour)
Registers an implementation of ITrueSyncBehaviour to be included in the simulation.
static List< TSPlayerInfo > Players
Returns the list of players connected.
Provides a few utilities to be used on TrueSync exposed classes.
GameObject[] playerPrefabs
Player prefabs to be instantiated in each machine.
static FP Time
Returns the time elapsed since the beginning of the simulation.
static Dictionary< string, MethodInfo > methodInfoByName
A few MethodInfo dictionary to allow reusable method calls.
static void RemovePlayer(int playerId)
Removes objets related to a provided player.
static int LastSafeTick
Returns the last safe simulated tick.
static void RegisterIsReadyChecker(TrueSyncIsReady IsReadyChecker)
Register a TrueSyncIsReady delegate to that returns true if the game can proceed or false otherwise...
Represents each player's behaviour simulated on every machine connected to the game.
static IPhysicsManager instance
Returns a proper implementation of IPhysicsManager.
static TrueSyncConfig Config
Returns the active TrueSyncConfig used by the TrueSyncManager.
static PlayerComparer playerComparer
Instance of a PlayerComparer.
Truesync's ICommunicator implementation based on PUN.
bool physics3DEnabled
Indicates if the 3D physics engine should be enabled.
static readonly TSVector zero
A vector with components (0,0,0);
TSVector gravity3D
Represents the simulated gravity.
static TSPlayerInfo LocalPlayer
Returns the local player.
FP z
The Z component of the vector.
static IPhysicsManager New(TrueSyncConfig trueSyncConfig)
Instantiates a new IPhysicsManager.
static int Ticks
Returns the number of the last simulated tick.
Abstract collider for 3D shapes.
Represents a set of configurations for TrueSync.
static TSVector Gravity
Returns the simulated gravity.
static void SyncedStartCoroutine(IEnumerator coroutine)
Starts a new coroutine.
int numberOfPlayers
Number of players connected to the game.
bool TSDisabled
If true the body doesn't interfere in physics simulation.
int panicWindow
Maximum number of ticks to wait until all other players inputs arrive.
bool showStats
When true shows a debug interface with a few information.
FP lockedTimeStep
Time between each TrueSync's frame.
TrueSync's communicator interface.
Manages physics simulation.
static GameObject SyncedInstantiate(GameObject prefab)
Instantiate a new prefab in a deterministic way.
static void PauseSimulation()
Pauses the game simulation.
FP x
The X component of the vector.
int ownerIndex
Index of the owner at initial players list.
static GameObject SyncedInstantiate(GameObject prefab, TSVector position, TSQuaternion rotation)
Instantiates a new prefab in a deterministic way.
IBody3D Body
Returns the body linked to this collider.
A Quaternion representing an orientation.
Abstract collider for 2D shapes.
static FP DeltaTime
Returns the deltaTime between two simulation calls.
static void RunSimulation()
Run/Unpause the game simulation.
bool physics2DEnabled
Indicates if the 2D physics engine should be enabled.
static void EndSimulation()
End the game simulation.
int syncWindow
Synchronization window size.
IBody2D Body
Returns RigidBody associated to this TSRigidBody.
static void SyncedDisableBehaviour(GameObject gameObject)
Disables 'OnSyncedInput' and 'OnSyncUpdate' calls to every ITrueSyncBehaviour attached.
Represents a common interface to 2D and 3D bodies.
static void SyncedDestroy(GameObject gameObject)
Destroys a GameObject in a deterministic way.
static GameObject SyncedInstantiate(GameObject prefab, TSVector2 position, TSQuaternion rotation)
Instantiates a new prefab in a deterministic way.