Files
OpenRA/OpenRA.Game/World.cs
Pavlos Touboulidis df0d1360dd Give PerfTimer the ability to write only slow operations to the log
This is useful to ignore fast operations that just spam the log.

The class had to be reworked because it couldn't properly handle cases
where all of a node's children where below the threshold.

Also changed DoTimed() and RunActivity() to use PerfTimer.
2014-05-22 03:40:36 +03:00

303 lines
7.6 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Orders;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA
{
public class World
{
internal TraitDictionary traitDict = new TraitDictionary();
HashSet<Actor> actors = new HashSet<Actor>();
List<IEffect> effects = new List<IEffect>();
Queue<Action<World>> frameEndActions = new Queue<Action<World>>();
public int Timestep;
internal readonly OrderManager orderManager;
public Session LobbyInfo { get { return orderManager.LobbyInfo; } }
public MersenneTwister SharedRandom;
public readonly List<Player> Players = new List<Player>();
public void AddPlayer(Player p) { Players.Add(p); }
public Player LocalPlayer { get; private set; }
Player renderPlayer;
public bool ObserveAfterWinOrLose;
public Player RenderPlayer
{
get { return renderPlayer == null || (ObserveAfterWinOrLose && renderPlayer.WinState != WinState.Undefined) ? null : renderPlayer; }
set { renderPlayer = value; }
}
public bool FogObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(a); }
public bool FogObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsVisible(p); }
public bool ShroudObscures(Actor a) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(a); }
public bool ShroudObscures(CPos p) { return RenderPlayer != null && !RenderPlayer.Shroud.IsExplored(p); }
public Rectangle? VisibleBounds
{
get
{
if (RenderPlayer == null)
return null;
return RenderPlayer.Shroud.ExploredBounds;
}
}
public bool IsReplay
{
get { return orderManager.Connection is ReplayConnection; }
}
public void SetLocalPlayer(string pr)
{
if (IsReplay)
return;
LocalPlayer = Players.FirstOrDefault(p => p.InternalName == pr);
RenderPlayer = LocalPlayer;
}
public readonly Actor WorldActor;
public readonly Map Map;
public readonly TileSet TileSet;
public readonly ActorMap ActorMap;
public readonly ScreenMap ScreenMap;
public void IssueOrder(Order o) { orderManager.IssueOrder(o); } /* avoid exposing the OM to mod code */
IOrderGenerator orderGenerator_;
public IOrderGenerator OrderGenerator
{
get { return orderGenerator_; }
set
{
Sync.AssertUnsynced("The current order generator may not be changed from synced code");
orderGenerator_ = value;
}
}
public Selection Selection = new Selection();
public void CancelInputMode() { OrderGenerator = new UnitOrderGenerator(); }
public bool ToggleInputMode<T>() where T : IOrderGenerator, new()
{
if (OrderGenerator is T)
{
CancelInputMode();
return false;
}
else
{
OrderGenerator = new T();
return true;
}
}
internal World(Map map, OrderManager orderManager, bool isShellmap)
{
IsShellmap = isShellmap;
this.orderManager = orderManager;
orderGenerator_ = new UnitOrderGenerator();
Map = map;
TileSet = map.Rules.TileSets[Map.Tileset];
SharedRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
WorldActor = CreateActor("World", new TypeDictionary());
ActorMap = WorldActor.Trait<ActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
// Add players
foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
cmp.CreatePlayers(this);
// Set defaults for any unset stances
foreach (var p in Players)
foreach (var q in Players)
if (!p.Stances.ContainsKey(q))
p.Stances[q] = Stance.Neutral;
Sound.SoundVolumeModifier = 1.0f;
}
public void LoadComplete(WorldRenderer wr)
{
foreach (var wlh in WorldActor.TraitsImplementing<IWorldLoaded>())
{
using (new Support.PerfTimer(wlh.GetType().Name + ".WorldLoaded"))
wlh.WorldLoaded(this, wr);
}
}
public Actor CreateActor(string name, TypeDictionary initDict)
{
return CreateActor(true, name, initDict);
}
public Actor CreateActor(bool addToWorld, string name, TypeDictionary initDict)
{
var a = new Actor(this, name, initDict);
if (addToWorld)
Add(a);
return a;
}
public void Add(Actor a)
{
a.IsInWorld = true;
actors.Add(a);
ActorAdded(a);
foreach (var t in a.TraitsImplementing<INotifyAddedToWorld>())
t.AddedToWorld(a);
}
public void Remove(Actor a)
{
a.IsInWorld = false;
actors.Remove(a);
ActorRemoved(a);
foreach (var t in a.TraitsImplementing<INotifyRemovedFromWorld>())
t.RemovedFromWorld(a);
}
public void Add(IEffect b) { effects.Add(b); }
public void Remove(IEffect b) { effects.Remove(b); }
public void AddFrameEndTask(Action<World> a) { frameEndActions.Enqueue(a); }
public event Action<Actor> ActorAdded = _ => { };
public event Action<Actor> ActorRemoved = _ => { };
public bool Paused { get; internal set; }
public bool PredictedPaused { get; internal set; }
public bool PauseStateLocked { get; set; }
public bool IsShellmap = false;
public int WorldTick { get; private set; }
public void SetPauseState(bool paused)
{
if (PauseStateLocked)
return;
IssueOrder(Order.PauseGame(paused));
PredictedPaused = paused;
}
public void SetLocalPauseState(bool paused)
{
Paused = PredictedPaused = paused;
}
public void Tick()
{
if (!Paused && (!IsShellmap || Game.Settings.Game.ShowShellmap))
{
WorldTick++;
using (new PerfSample("tick_idle"))
foreach (var ni in ActorsWithTrait<INotifyIdle>())
if (ni.Actor.IsIdle)
ni.Trait.TickIdle(ni.Actor);
using (new PerfSample("tick_activities"))
foreach (var a in actors)
a.Tick();
ActorsWithTrait<ITick>().DoTimed(x => x.Trait.Tick(x.Actor), "Trait", Game.Settings.Debug.LongTickThreshold);
effects.DoTimed(e => e.Tick(this), "Effect", Game.Settings.Debug.LongTickThreshold);
}
while (frameEndActions.Count != 0)
frameEndActions.Dequeue()(this);
}
// For things that want to update their render state once per tick, ignoring pause state
public void TickRender(WorldRenderer wr)
{
ActorsWithTrait<ITickRender>().Do(x => x.Trait.TickRender(wr, x.Actor));
}
public IEnumerable<Actor> Actors { get { return actors; } }
public IEnumerable<IEffect> Effects { get { return effects; } }
uint nextAID = 0;
internal uint NextAID()
{
return nextAID++;
}
public int SyncHash()
{
//using (new PerfSample("synchash"))
{
int n = 0;
int ret = 0;
// hash all the actors
foreach (var a in Actors)
ret += n++ * (int)(1 + a.ActorID) * Sync.CalculateSyncHash(a);
// hash all the traits that tick
foreach (var x in traitDict.ActorsWithTraitMultiple<ISync>(this))
ret += n++ * (int)(1 + x.Actor.ActorID) * Sync.CalculateSyncHash(x.Trait);
// TODO: don't go over all effects
foreach (var e in Effects)
{
var sync = e as ISync;
if (sync != null)
ret += n++ * Sync.CalculateSyncHash(sync);
}
// Hash the shared rng
ret += SharedRandom.Last;
return ret;
}
}
public IEnumerable<TraitPair<T>> ActorsWithTrait<T>()
{
return traitDict.ActorsWithTraitMultiple<T>(this);
}
}
public struct TraitPair<T>
{
public Actor Actor;
public T Trait;
public override string ToString()
{
return "{0}->{1}".F(Actor.Info.Name, Trait.GetType().Name);
}
}
}