Merge branch 'master' of git://github.com/chrisforbes/OpenRA
This commit is contained in:
@@ -46,5 +46,10 @@ namespace OpenRa
|
||||
if( i.Value is T )
|
||||
yield return (T)i.Value;
|
||||
}
|
||||
|
||||
public IEnumerator<object> GetEnumerator()
|
||||
{
|
||||
return WithInterface<object>().GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,16 @@ namespace OpenRa.Game
|
||||
{
|
||||
class Actor
|
||||
{
|
||||
[Sync]
|
||||
public readonly TypeDictionary traits = new TypeDictionary();
|
||||
public readonly UnitInfo Info;
|
||||
|
||||
public readonly uint ActorID;
|
||||
[Sync]
|
||||
public int2 Location;
|
||||
[Sync]
|
||||
public Player Owner;
|
||||
[Sync]
|
||||
public int Health;
|
||||
IActivity currentActivity;
|
||||
|
||||
|
||||
@@ -459,7 +459,7 @@ namespace OpenRa.Game
|
||||
shpRenderer.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DrawChat()
|
||||
{
|
||||
var chatpos = new int2(400, Game.viewport.Height - 20);
|
||||
|
||||
@@ -147,14 +147,24 @@ namespace OpenRa.Game
|
||||
|
||||
public Cursor ChooseCursor()
|
||||
{
|
||||
var mi = new MouseInput
|
||||
{
|
||||
Location = (Game.CellSize * MousePosition - Game.viewport.Location).ToInt2(),
|
||||
Button = MouseButton.Right,
|
||||
Modifiers = GetModifierKeys(),
|
||||
};
|
||||
int sync = Game.world.SyncHash();
|
||||
|
||||
return orderGenerator.GetCursor(MousePosition.ToInt2(), mi);
|
||||
try
|
||||
{
|
||||
var mi = new MouseInput
|
||||
{
|
||||
Location = ( Game.CellSize * MousePosition - Game.viewport.Location ).ToInt2(),
|
||||
Button = MouseButton.Right,
|
||||
Modifiers = GetModifierKeys(),
|
||||
};
|
||||
|
||||
return orderGenerator.GetCursor( MousePosition.ToInt2(), mi );
|
||||
}
|
||||
finally
|
||||
{
|
||||
if( sync != Game.world.SyncHash() )
|
||||
throw new InvalidOperationException( "Desync in Controller.ChooseCursor" );
|
||||
}
|
||||
}
|
||||
|
||||
Cache<int, List<Actor>> controlGroups = new Cache<int, List<Actor>>(_ => new List<Actor>());
|
||||
|
||||
0
OpenRa.Game/Effects/Bullet.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Bullet.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Corpse.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Corpse.cs
Normal file → Executable file
0
OpenRa.Game/Effects/DelayedAction.cs
Normal file → Executable file
0
OpenRa.Game/Effects/DelayedAction.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Explosion.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Explosion.cs
Normal file → Executable file
0
OpenRa.Game/Effects/FlashTarget.cs
Normal file → Executable file
0
OpenRa.Game/Effects/FlashTarget.cs
Normal file → Executable file
0
OpenRa.Game/Effects/GpsSatellite.cs
Normal file → Executable file
0
OpenRa.Game/Effects/GpsSatellite.cs
Normal file → Executable file
0
OpenRa.Game/Effects/IEffect.cs
Normal file → Executable file
0
OpenRa.Game/Effects/IEffect.cs
Normal file → Executable file
0
OpenRa.Game/Effects/InvulnEffect.cs
Normal file → Executable file
0
OpenRa.Game/Effects/InvulnEffect.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Missile.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Missile.cs
Normal file → Executable file
0
OpenRa.Game/Effects/MoveFlash.cs
Normal file → Executable file
0
OpenRa.Game/Effects/MoveFlash.cs
Normal file → Executable file
0
OpenRa.Game/Effects/PowerDownIndicator.cs
Normal file → Executable file
0
OpenRa.Game/Effects/PowerDownIndicator.cs
Normal file → Executable file
0
OpenRa.Game/Effects/RepairIndicator.cs
Normal file → Executable file
0
OpenRa.Game/Effects/RepairIndicator.cs
Normal file → Executable file
0
OpenRa.Game/Effects/SatelliteLaunch.cs
Normal file → Executable file
0
OpenRa.Game/Effects/SatelliteLaunch.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Smoke.cs
Normal file → Executable file
0
OpenRa.Game/Effects/Smoke.cs
Normal file → Executable file
@@ -95,6 +95,8 @@ namespace OpenRa.Game
|
||||
|
||||
void DispatchMouseInput(MouseInputEvent ev, MouseEventArgs e)
|
||||
{
|
||||
int sync = Game.world.SyncHash();
|
||||
|
||||
Game.viewport.DispatchMouseInput(
|
||||
new MouseInput
|
||||
{
|
||||
@@ -103,6 +105,9 @@ namespace OpenRa.Game
|
||||
Location = new int2(e.Location),
|
||||
Modifiers = (Modifiers)(int)ModifierKeys,
|
||||
});
|
||||
|
||||
if( sync != Game.world.SyncHash() )
|
||||
throw new InvalidOperationException( "Desync in DispatchMouseInput" );
|
||||
}
|
||||
|
||||
protected override void OnMouseDown(MouseEventArgs e)
|
||||
@@ -136,6 +141,8 @@ namespace OpenRa.Game
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
|
||||
int sync = Game.world.SyncHash();
|
||||
|
||||
/* hack hack hack */
|
||||
if (e.KeyCode == Keys.F8 && !Game.orderManager.GameStarted)
|
||||
{
|
||||
@@ -156,16 +163,24 @@ namespace OpenRa.Game
|
||||
if (!Game.chat.isChatting)
|
||||
if (e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9)
|
||||
Game.controller.DoControlGroup( (int)e.KeyCode - (int)Keys.D0, (Modifiers)(int)e.Modifiers );
|
||||
|
||||
if( sync != Game.world.SyncHash() )
|
||||
throw new InvalidOperationException( "Desync in OnKeyDown" );
|
||||
}
|
||||
|
||||
protected override void OnKeyPress(KeyPressEventArgs e)
|
||||
{
|
||||
base.OnKeyPress(e);
|
||||
|
||||
int sync = Game.world.SyncHash();
|
||||
|
||||
if (e.KeyChar == '\r')
|
||||
Game.chat.Toggle();
|
||||
else if (Game.chat.isChatting)
|
||||
Game.chat.TypeChar(e.KeyChar);
|
||||
|
||||
if( sync != Game.world.SyncHash() )
|
||||
throw new InvalidOperationException( "Desync in OnKeyPress" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@
|
||||
<Compile Include="SupportPowers\NullPower.cs" />
|
||||
<Compile Include="Support\Stopwatch.cs" />
|
||||
<Compile Include="Support\PerfHistory.cs" />
|
||||
<Compile Include="Sync.cs" />
|
||||
<Compile Include="Traits\AcceptsOre.cs" />
|
||||
<Compile Include="Traits\Activities\Attack.cs" />
|
||||
<Compile Include="Traits\Activities\CaptureBuilding.cs" />
|
||||
@@ -318,4 +319,4 @@
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -30,7 +30,7 @@ namespace OpenRa.Game
|
||||
this.SubjectId = subjectId;
|
||||
this.TargetActorId = targetActorId;
|
||||
this.TargetLocation = targetLocation;
|
||||
this.TargetString = targetString;
|
||||
this.TargetString = targetString;
|
||||
}
|
||||
|
||||
public bool Validate()
|
||||
|
||||
107
OpenRa.Game/Sync.cs
Executable file
107
OpenRa.Game/Sync.cs
Executable file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using IjwFramework.Collections;
|
||||
|
||||
namespace OpenRa.Game
|
||||
{
|
||||
class SyncAttribute : Attribute { }
|
||||
|
||||
static class Sync
|
||||
{
|
||||
static Cache<Type, Func<object, int>> hashFuncCache = new Cache<Type, Func<object, int>>( t => GenerateHashFunc( t ) );
|
||||
|
||||
public static int CalculateSyncHash( object obj )
|
||||
{
|
||||
return hashFuncCache[ obj.GetType() ]( obj );
|
||||
}
|
||||
|
||||
public static Func<object,int> GenerateHashFunc( Type t )
|
||||
{
|
||||
var d = new DynamicMethod( "hash_{0}".F( t.Name ), typeof( int ), new Type[] { typeof( object ) }, t );
|
||||
var il = d.GetILGenerator();
|
||||
var this_ = il.DeclareLocal( t ).LocalIndex;
|
||||
il.Emit( OpCodes.Ldarg_0 );
|
||||
il.Emit( OpCodes.Castclass, t );
|
||||
il.Emit( OpCodes.Stloc, this_ );
|
||||
il.Emit( OpCodes.Ldc_I4_0 );
|
||||
|
||||
const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
foreach( var field in t.GetFields( bf ).Where( x => x.GetCustomAttributes( typeof( SyncAttribute ), true ).Length != 0 ) )
|
||||
{
|
||||
il.Emit( OpCodes.Ldloc, this_ );
|
||||
il.Emit( OpCodes.Ldfld, field );
|
||||
|
||||
if( field.FieldType == typeof( int ) )
|
||||
{
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else if( field.FieldType == typeof( bool ) )
|
||||
{
|
||||
var l = il.DefineLabel();
|
||||
il.Emit( OpCodes.Ldc_I4, 0xaaa );
|
||||
il.Emit( OpCodes.Brtrue, l );
|
||||
il.Emit( OpCodes.Pop );
|
||||
il.Emit( OpCodes.Ldc_I4, 0x555 );
|
||||
il.MarkLabel( l );
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else if( field.FieldType == typeof( int2 ) )
|
||||
{
|
||||
il.EmitCall( OpCodes.Call, ( (Func<int2, int>)hash_int2 ).Method, null );
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else if( field.FieldType == typeof( TypeDictionary ) )
|
||||
{
|
||||
il.EmitCall( OpCodes.Call, ( (Func<TypeDictionary, int>)hash_tdict ).Method, null );
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else if( field.FieldType == typeof( Actor ) )
|
||||
{
|
||||
il.EmitCall( OpCodes.Call, ( (Func<Actor, int>)hash_actor ).Method, null );
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else if( field.FieldType == typeof( Player ) )
|
||||
{
|
||||
il.EmitCall( OpCodes.Call, ( (Func<Player, int>)hash_player ).Method, null );
|
||||
il.Emit( OpCodes.Xor );
|
||||
}
|
||||
else
|
||||
throw new NotImplementedException( "SyncAttribute on unhashable field" );
|
||||
}
|
||||
|
||||
il.Emit( OpCodes.Ret );
|
||||
return (Func<object,int>)d.CreateDelegate( typeof( Func<object,int> ) );
|
||||
}
|
||||
|
||||
internal static int hash_int2( int2 i2 )
|
||||
{
|
||||
return ( ( i2.X * 5 ) ^ ( i2.Y * 3 ) ) / 4;
|
||||
}
|
||||
|
||||
internal static int hash_tdict( TypeDictionary d )
|
||||
{
|
||||
int ret = 0;
|
||||
foreach( var o in d )
|
||||
ret += CalculateSyncHash( o );
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal static int hash_actor( Actor a )
|
||||
{
|
||||
if( a != null )
|
||||
return (int)( a.ActorID << 16 );
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal static int hash_player( Player p )
|
||||
{
|
||||
if( p != null )
|
||||
return p.Index * 0x567;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,12 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class AttackBase : IIssueOrder, IResolveOrder, ITick
|
||||
{
|
||||
public Actor target;
|
||||
[Sync] public Actor target;
|
||||
|
||||
// time (in frames) until each weapon can fire again.
|
||||
[Sync]
|
||||
protected int primaryFireDelay = 0;
|
||||
[Sync]
|
||||
protected int secondaryFireDelay = 0;
|
||||
|
||||
int primaryBurst;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace OpenRa.Game.Traits
|
||||
public AttackFrontal(Actor self, int facingTolerance)
|
||||
: base(self) { FacingTolerance = facingTolerance; }
|
||||
|
||||
int FacingTolerance;
|
||||
readonly int FacingTolerance;
|
||||
|
||||
public override void Tick(Actor self)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,9 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
readonly Actor self;
|
||||
public readonly BuildingInfo unitInfo;
|
||||
[Sync]
|
||||
bool isRepairing = false;
|
||||
[Sync]
|
||||
bool manuallyDisabled = false;
|
||||
public bool ManuallyDisabled { get { return manuallyDisabled; } }
|
||||
public bool Disabled { get { return (manuallyDisabled || (unitInfo.Powered && self.Owner.GetPowerState() != PowerState.Normal)); } }
|
||||
|
||||
@@ -7,8 +7,9 @@ namespace OpenRa.Game.Traits
|
||||
class ChronoshiftDeploy : IIssueOrder, IResolveOrder, ISpeedModifier, ITick, IPips
|
||||
{
|
||||
// Recharge logic
|
||||
[Sync]
|
||||
int chargeTick = 0; // How long until we can chronoshift again?
|
||||
int chargeLength = (int)(Rules.Aftermath.ChronoTankDuration * 60 * 25); // How long between shifts?
|
||||
readonly int chargeLength = (int)(Rules.Aftermath.ChronoTankDuration * 60 * 25); // How long between shifts?
|
||||
|
||||
public ChronoshiftDeploy(Actor self) { }
|
||||
|
||||
|
||||
@@ -8,9 +8,11 @@ namespace OpenRa.Game.Traits
|
||||
class Chronoshiftable : IResolveOrder, ISpeedModifier, ITick
|
||||
{
|
||||
// Return-to-sender logic
|
||||
[Sync]
|
||||
int2 chronoshiftOrigin;
|
||||
[Sync]
|
||||
int chronoshiftReturnTicks = 0;
|
||||
|
||||
|
||||
public Chronoshiftable(Actor self) { }
|
||||
|
||||
public void Tick(Actor self)
|
||||
@@ -65,7 +67,7 @@ namespace OpenRa.Game.Traits
|
||||
self.QueueActivity(new Activities.Teleport(order.TargetLocation));
|
||||
|
||||
var power = self.Owner.SupportPowers[order.TargetString].Impl;
|
||||
power.OnFireNotification(self, self.Location);
|
||||
power.OnFireNotification(self, self.Location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class Cloak : IRenderModifier, INotifyAttack, ITick
|
||||
{
|
||||
[Sync]
|
||||
int remainingUncloakTime = 2; /* setup for initial cloak */
|
||||
|
||||
public Cloak(Actor self) {}
|
||||
|
||||
@@ -5,7 +5,9 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class Harvester : IIssueOrder, IResolveOrder, IPips
|
||||
{
|
||||
[Sync]
|
||||
public int oreCarried = 0; /* sum of these must not exceed capacity */
|
||||
[Sync]
|
||||
public int gemsCarried = 0;
|
||||
|
||||
public bool IsFull { get { return oreCarried + gemsCarried == Rules.General.BailCount; } }
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class IronCurtainable : IResolveOrder, IDamageModifier, ITick
|
||||
{
|
||||
[Sync]
|
||||
int RemainingTicks = 0;
|
||||
|
||||
public IronCurtainable(Actor self) { }
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class LimitedAmmo : INotifyAttack, IPips
|
||||
{
|
||||
[Sync]
|
||||
int ammo;
|
||||
Actor self;
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
readonly Actor self;
|
||||
|
||||
[Sync]
|
||||
int2 __fromCell;
|
||||
public int2 fromCell
|
||||
{
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace OpenRa.Game.Traits
|
||||
}
|
||||
|
||||
// Key: Production category.
|
||||
// TODO: sync this
|
||||
readonly Cache<string, List<ProductionItem>> production
|
||||
= new Cache<string, List<ProductionItem>>( _ => new List<ProductionItem>() );
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class RallyPoint : IRender, IIssueOrder, IResolveOrder, ITick
|
||||
{
|
||||
[Sync]
|
||||
public int2 rallyPoint;
|
||||
public Animation anim;
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ namespace OpenRa.Game.Traits
|
||||
class RenderWarFactory : IRender, INotifyBuildComplete, INotifyDamage, ITick, INotifyProduction
|
||||
{
|
||||
public Animation roof;
|
||||
[Sync]
|
||||
bool doneBuilding;
|
||||
[Sync]
|
||||
bool isOpen;
|
||||
public readonly Actor self;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class Submarine : IRenderModifier, INotifyAttack, ITick, INotifyDamage
|
||||
{
|
||||
[Sync]
|
||||
int remainingSurfaceTime = 2; /* setup for initial dive */
|
||||
|
||||
public Submarine(Actor self) { }
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace OpenRa.Game.Traits
|
||||
const float proneDamage = .5f;
|
||||
const float proneSpeed = .5f;
|
||||
|
||||
[Sync]
|
||||
int remainingProneTime = 0;
|
||||
|
||||
public bool IsProne { get { return remainingProneTime > 0; } }
|
||||
|
||||
@@ -10,18 +10,18 @@ namespace OpenRa.Game.Traits
|
||||
// depends on the order of pips in WorldRenderer.cs!
|
||||
enum PipType { Transparent, Green, Yellow, Red, Gray };
|
||||
enum TagType { None, Fake, Primary };
|
||||
|
||||
|
||||
interface ITick { void Tick(Actor self); }
|
||||
interface IRender { IEnumerable<Renderable> Render(Actor self); }
|
||||
interface IIssueOrder { Order IssueOrder( Actor self, int2 xy, MouseInput mi, Actor underCursor ); }
|
||||
interface IResolveOrder { void ResolveOrder( Actor self, Order order ); }
|
||||
|
||||
interface INotifySold { void Sold(Actor self); }
|
||||
interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
|
||||
interface INotifyBuildComplete { void BuildingComplete (Actor self); }
|
||||
interface INotifyProduction { void UnitProduced(Actor self, Actor other); }
|
||||
interface IAcceptThief { void OnSteal(Actor self, Actor thief); }
|
||||
|
||||
interface IIssueOrder { Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor); }
|
||||
interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
|
||||
|
||||
interface IProducer
|
||||
{
|
||||
bool Produce( Actor self, UnitInfo producee );
|
||||
|
||||
@@ -3,6 +3,7 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class Turreted : ITick
|
||||
{
|
||||
[Sync]
|
||||
public int turretFacing = 0;
|
||||
public int? desiredFacing;
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ namespace OpenRa.Game.Traits
|
||||
{
|
||||
class Unit : INotifyDamage
|
||||
{
|
||||
[Sync]
|
||||
public int Facing;
|
||||
[Sync]
|
||||
public int Altitude;
|
||||
|
||||
public Unit( Actor self ) { }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenRa.Game.Effects;
|
||||
using OpenRa.Game.Support;
|
||||
|
||||
namespace OpenRa.Game
|
||||
{
|
||||
@@ -12,15 +13,15 @@ namespace OpenRa.Game
|
||||
|
||||
public void Add(Actor a)
|
||||
{
|
||||
a.IsInWorld = true;
|
||||
actors.Add(a);
|
||||
a.IsInWorld = true;
|
||||
actors.Add(a);
|
||||
ActorAdded(a);
|
||||
}
|
||||
|
||||
public void Remove(Actor a)
|
||||
{
|
||||
a.IsInWorld = false;
|
||||
actors.Remove(a);
|
||||
a.IsInWorld = false;
|
||||
actors.Remove(a);
|
||||
ActorRemoved(a);
|
||||
}
|
||||
|
||||
@@ -52,5 +53,17 @@ namespace OpenRa.Game
|
||||
{
|
||||
return nextAID++;
|
||||
}
|
||||
|
||||
public int SyncHash()
|
||||
{
|
||||
using (new PerfSample("synchash"))
|
||||
{
|
||||
int ret = 0;
|
||||
foreach (var a in Actors)
|
||||
ret += (int)a.ActorID * Sync.CalculateSyncHash(a);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user