public GameAttributeDependency(GameAttribute attribute, int? key, bool usesExplicitKey, bool isManualDependency) { Attribute = attribute; Key = key; UsesExplicitKey = usesExplicitKey; IsManualDependency = isManualDependency; }
public GameAttributeDependency(GameAttribute attribute, int?key, bool usesExplicitKey, bool isManualDependency) { Attribute = attribute; Key = key; UsesExplicitKey = usesExplicitKey; IsManualDependency = isManualDependency; }
private void RawSetAttributeValue(GameAttribute attribute, int? key, GameAttributeValue value) { KeyId keyid; keyid.Id = attribute.Id; keyid.Key = key; _attributeValues[keyid] = value; if (!_changedAttributes.Contains(keyid)) _changedAttributes.Add(keyid); // mark dependant attributes as changed if (attribute.Dependents != null) { foreach (var dependent in attribute.Dependents) { int? usekey; if (dependent.IsManualDependency) usekey = dependent.Key; else usekey = dependent.UsesExplicitKey ? null : key; if (dependent.IsManualDependency || dependent.UsesExplicitKey == false || dependent.Key == key) { // TODO: always update dependent values for now, but eventually make this lazy RawSetAttributeValue(dependent.Attribute, usekey, dependent.Attribute.ScriptFunc(this, usekey)); } } } }
private void SetAttributeValue(GameAttribute attribute, int? key, GameAttributeValue value) { // error if scripted attribute and is not settable if (attribute.ScriptFunc != null && !attribute.ScriptedAndSettable) { var frame = new System.Diagnostics.StackFrame(2, true); Logger.Error("illegal value assignment for GameAttribute.{0} attempted at {1}:{2}", attribute.Name, frame.GetFileName(), frame.GetFileLineNumber()); } if (attribute.EncodingType == GameAttributeEncoding.IntMinMax) { if (value.Value < attribute.Min.Value || value.Value > attribute.Max.Value) throw new ArgumentOutOfRangeException("GameAttribute." + attribute.Name.Replace(' ', '_'), "Min: " + attribute.Min.Value + " Max: " + attribute.Max.Value + " Tried to set: " + value.Value); } else if (attribute.EncodingType == GameAttributeEncoding.Float16) { if (value.ValueF < GameAttribute.Float16Min || value.ValueF > GameAttribute.Float16Max) throw new ArgumentOutOfRangeException("GameAttribute." + attribute.Name.Replace(' ', '_'), "Min: " + GameAttribute.Float16Min + " Max " + GameAttribute.Float16Max + " Tried to set: " + value.ValueF); } RawSetAttributeValue(attribute, key, value); }
private GameAttributeValue RawGetAttributeValue(GameAttribute attribute, int? key) { KeyId keyid; keyid.Id = attribute.Id; keyid.Key = key; GameAttributeValue gaValue; if (_attributeValues.TryGetValue(keyid, out gaValue)) return gaValue; return attribute._DefaultValue; }
private GameAttributeValue GetAttributeValue(GameAttribute attribute, int? key) { if (attribute.ScriptFunc != null) return attribute.ScriptFunc(this, key); else return RawGetAttributeValue(attribute, key); }
void SetAttributeValue(GameAttribute attribute, int? key, GameAttributeValue value) { KeyId keyid; keyid.Id = attribute.Id; keyid.Key = key; if (attribute.EncodingType == GameAttributeEncoding.IntMinMax) { if (value.Value < attribute.Min.Value || value.Value > attribute.Max.Value) throw new ArgumentOutOfRangeException("GameAttribute." + attribute.Name.Replace(' ', '_'), "Min: " + attribute.Min.Value + " Max: " + attribute.Max.Value + " Tried to set: " + value.Value); } else if (attribute.EncodingType == GameAttributeEncoding.Float16) { if (value.ValueF < GameAttribute.Float16Min || value.ValueF > GameAttribute.Float16Max) throw new ArgumentOutOfRangeException("GameAttribute." + attribute.Name.Replace(' ', '_'), "Min: " + GameAttribute.Float16Min + " Max " + GameAttribute.Float16Max + " Tried to set: " + value.ValueF); } _attributeValues[keyid] = value; }
public int?[] AttributeKeys(GameAttribute ga) { return _attributeValues.Where(av => av.Key.Id == ga.Id).Select(av => av.Key.Key).ToArray(); }
public static void ProcessAttributes(GameAttribute[] attributes) { // build string -> GameAttribute lookup var attributeLookup = attributes.ToDictionary(attr => attr.Name); // will contain C# code for the func<> body that represents each attribute's script. var csharpScripts = new Dictionary <GameAttribute, string>(); // generate C#-compatible source lines from scripts and create attribute dependency lists foreach (GameAttribute attr in attributes) { // check for valid script in the attribute and select it string script; if (attr.ScriptA.Length > 0 && attr.ScriptA != "0") { script = attr.ScriptA; } else if (attr.ScriptB.Length > 0 && attr.ScriptB != "0") { script = attr.ScriptB; } else { continue; // no valid script, done processing this attribute } // by default all scripts are not settable // can be set to true if self-referring identifier is found attr.ScriptedAndSettable = false; // replace attribute references with GameAttributeMap lookups // also record all attributes used by script into each attribute's dependency list script = Regex.Replace(script, @"([A-Za-z_]\w*)(\.Agg)?(\#[A-Za-z_]\w*)?(?=[^\(\w]|\z)( \?)?", (match) => { // lookup attribute object string identifierName = match.Groups[1].Value; if (!attributeLookup.ContainsKey(identifierName)) { throw new ScriptedAttributeInitializerError("invalid identifer parsed: " + identifierName); } GameAttribute identifier = attributeLookup[identifierName]; // key selection int?key = null; string keyString = "_key"; bool usesExplicitKey = false; if (match.Groups[3].Success) { switch (match.Groups[3].Value.ToUpper()) { case "#NONE": key = null; break; case "#PHYSICAL": key = 0; break; case "#FIRE": key = 1; break; case "#LIGHTNING": key = 2; break; case "#COLD": key = 3; break; case "#POISON": key = 4; break; case "#ARCANE": key = 5; break; case "#HOLY": key = 6; break; default: throw new ScriptedAttributeInitializerError("error processing attribute script, invalid key in identifier: " + match.Groups[3].Value); } if (key == null) { keyString = "null"; } else { keyString = key.ToString(); } usesExplicitKey = true; } // add comparsion for int attributes that are directly used in an ?: expression. string compare = ""; if (match.Groups[4].Success) { compare = identifier is GameAttributeI ? " > 0 ?" : " ?"; } // handle self-referring lookup. example: Resource.Agg if (match.Groups[2].Success) { attr.ScriptedAndSettable = true; return("_map._RawGetAttribute(GameAttribute." + identifierName + ", " + keyString + ")" + compare); } // record dependency if (identifier.Dependents == null) { identifier.Dependents = new List <GameAttributeDependency>(); } identifier.Dependents.Add(new GameAttributeDependency(attr, key, usesExplicitKey, false)); // generate normal lookup return("_map[GameAttribute." + identifierName + ", " + keyString + "]" + compare); }); // transform function calls into C# equivalents script = Regex.Replace(script, @"floor\(", "(float)Math.Floor(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"max\(", "Math.Max(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"min\(", "Math.Min(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"pin\(", "ScriptedAttributeInitializer.Pin(", RegexOptions.IgnoreCase); // add C# single-precision affix to decimal literals. example: 1.25 => 1.25f script = Regex.Replace(script, @"\d+\.\d+", "$0f"); csharpScripts[attr] = script; } // generate and write final C# code to file string sourcePathBase = Path.Combine(Path.GetTempPath(), "MooegeScriptedAttributeFuncs"); using (StreamWriter fout = new StreamWriter(sourcePathBase + ".cs")) { fout.Write( @"// This file was auto-generated by Mooege class ScriptedAttributeInitializer // It contains Funcs derived from GameAttribute.ScriptA/B scripts. // Funcs will be assigned to their respective GameAttribute.ScriptFunc member. using System; using Mooege.Net.GS.Message; using Mooege.Core.GS.Objects; namespace Mooege.Net.GS.Message.GeneratedCode { public class ScriptedAttributeFuncs { "); foreach (var scriptEntry in csharpScripts) { // select output type cast to ensure it matches attribute type string castType = scriptEntry.Key is GameAttributeF ? "float" : "int"; // write out full Func static class field fout.WriteLine(" public static Func<GameAttributeMap, int?, GameAttributeValue> {0} = (_map, _key) => new GameAttributeValue(({1})({2}));", scriptEntry.Key.Name, castType, scriptEntry.Value); } fout.Write( @" } } "); } // compile code var options = new CompilerParameters(); options.GenerateExecutable = false; options.OutputAssembly = sourcePathBase + ".dll"; options.IncludeDebugInformation = true; options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); var results = new CSharpCodeProvider().CompileAssemblyFromFile(options, sourcePathBase + ".cs"); if (results.Errors.Count > 0) { StringBuilder emsg = new StringBuilder(); emsg.AppendLine("encountered errors compiling attribute funcs:"); foreach (var e in results.Errors) { emsg.AppendLine(e.ToString()); } throw new ScriptedAttributeInitializerError(emsg.ToString()); } // pull funcs from new assembly and assign them to their respective attributes Type funcs = results.CompiledAssembly.GetType("Mooege.Net.GS.Message.GeneratedCode.ScriptedAttributeFuncs"); foreach (var attr in csharpScripts.Keys) { attr.ScriptFunc = (Func <GameAttributeMap, int?, GameAttributeValue>)funcs .GetField(attr.Name).GetValue(null); } }
public static void ProcessAttributes(GameAttribute[] attributes) { // build string -> GameAttribute lookup var attributeLookup = attributes.ToDictionary(attr => attr.Name); // will contain C# code for the func<> body that represents each attribute's script. var csharpScripts = new Dictionary<GameAttribute, string>(); // generate C#-compatible source lines from scripts and create attribute dependency lists foreach (GameAttribute attr in attributes) { // check for valid script in the attribute and select it string script; if (attr.ScriptA.Length > 0 && attr.ScriptA != "0") script = attr.ScriptA; else if (attr.ScriptB.Length > 0 && attr.ScriptB != "0") script = attr.ScriptB; else continue; // no valid script, done processing this attribute // by default all scripts are not settable // can be set to true if self-referring identifier is found attr.ScriptedAndSettable = false; // replace attribute references with GameAttributeMap lookups // also record all attributes used by script into each attribute's dependency list script = Regex.Replace(script, @"([A-Za-z_]\w*)(\.Agg)?(\#[A-Za-z_]\w*)?(?=[^\(\w]|\z)( \?)?", (match) => { // lookup attribute object string identifierName = match.Groups[1].Value; if (!attributeLookup.ContainsKey(identifierName)) throw new ScriptedAttributeInitializerError("invalid identifer parsed: " + identifierName); GameAttribute identifier = attributeLookup[identifierName]; // key selection int? key = null; string keyString = "_key"; bool usesExplicitKey = false; if (match.Groups[3].Success) { switch (match.Groups[3].Value.ToUpper()) { case "#NONE": key = null; break; case "#PHYSICAL": key = 0; break; case "#FIRE": key = 1; break; case "#LIGHTNING": key = 2; break; case "#COLD": key = 3; break; case "#POISON": key = 4; break; case "#ARCANE": key = 5; break; case "#HOLY": key = 6; break; default: throw new ScriptedAttributeInitializerError("error processing attribute script, invalid key in identifier: " + match.Groups[3].Value); } if (key == null) keyString = "null"; else keyString = key.ToString(); usesExplicitKey = true; } // add comparsion for int attributes that are directly used in an ?: expression. string compare = ""; if (match.Groups[4].Success) compare = identifier is GameAttributeI ? " > 0 ?" : " ?"; // handle self-referring lookup. example: Resource.Agg if (match.Groups[2].Success) { attr.ScriptedAndSettable = true; return "_map._RawGetAttribute(GameAttribute." + identifierName + ", " + keyString + ")" + compare; } // record dependency if (identifier.Dependents == null) identifier.Dependents = new List<GameAttributeDependency>(); identifier.Dependents.Add(new GameAttributeDependency(attr, key, usesExplicitKey, false)); // generate normal lookup return "_map[GameAttribute." + identifierName + ", " + keyString + "]" + compare; }); // transform function calls into C# equivalents script = Regex.Replace(script, @"floor\(", "(float)Math.Floor(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"max\(", "Math.Max(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"min\(", "Math.Min(", RegexOptions.IgnoreCase); script = Regex.Replace(script, @"pin\(", "ScriptedAttributeInitializer.Pin(", RegexOptions.IgnoreCase); // add C# single-precision affix to decimal literals. example: 1.25 => 1.25f script = Regex.Replace(script, @"\d+\.\d+", "$0f"); csharpScripts[attr] = script; } // generate and write final C# code to file string sourcePathBase = Path.Combine(Path.GetTempPath(), "MooegeScriptedAttributeFuncs"); using (StreamWriter fout = new StreamWriter(sourcePathBase + ".cs")) { fout.Write( @"// This file was auto-generated by Mooege class ScriptedAttributeInitializer // It contains Funcs derived from GameAttribute.ScriptA/B scripts. // Funcs will be assigned to their respective GameAttribute.ScriptFunc member. using System; using Mooege.Net.GS.Message; using Mooege.Core.GS.Objects; namespace Mooege.Net.GS.Message.GeneratedCode { public class ScriptedAttributeFuncs { "); foreach (var scriptEntry in csharpScripts) { // select output type cast to ensure it matches attribute type string castType = scriptEntry.Key is GameAttributeF ? "float" : "int"; // write out full Func static class field fout.WriteLine(" public static Func<GameAttributeMap, int?, GameAttributeValue> {0} = (_map, _key) => new GameAttributeValue(({1})({2}));", scriptEntry.Key.Name, castType, scriptEntry.Value); } fout.Write( @" } } "); } // compile code var options = new CompilerParameters(); options.GenerateExecutable = false; options.OutputAssembly = sourcePathBase + ".dll"; options.IncludeDebugInformation = true; options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); var results = new CSharpCodeProvider().CompileAssemblyFromFile(options, sourcePathBase + ".cs"); if (results.Errors.Count > 0) { StringBuilder emsg = new StringBuilder(); emsg.AppendLine("encountered errors compiling attribute funcs:"); foreach (var e in results.Errors) emsg.AppendLine(e.ToString()); throw new ScriptedAttributeInitializerError(emsg.ToString()); } // pull funcs from new assembly and assign them to their respective attributes Type funcs = results.CompiledAssembly.GetType("Mooege.Net.GS.Message.GeneratedCode.ScriptedAttributeFuncs"); foreach (var attr in csharpScripts.Keys) { attr.ScriptFunc = (Func<GameAttributeMap, int?, GameAttributeValue>)funcs .GetField(attr.Name).GetValue(null); } }