Since I got asked about this in an email, figured I may as well share them out.
Dogecoin: DS6JemiJDdjjDkL7u45VgzHweLCeaMdX9n
Bitcoin: 19kLLWWexwCk1N1KjedNEDhXxnSWF54r2J
Do with that what you will.
December 28, 2013
December 22, 2013
C++ 11 and Types
I've been trying to ramp up on all the language changes as part of C++ 11, and have been trying to figure out how to do something similar to XNA's Content.Load<T> as part of it, and I'm a bit stymied so far. Let me explain.
XNA's ContentManager has a ton of features that seem like they'd work wonderfully in C++'s RAII world. When you call .Load<T>, it first checks to see if the content has already been loaded through this particular ContentManager. If it has, you get the cached copy back. Otherwise, it tries to load the file, instantiates the proper type loader, caches the loaded object, then returns a reference to the object. When ContentManager is disposed of, it handles cleaning up all of its cached objects for you. Plus, I can keep multiple ContentManagers around: one for "common" items (characters, UX elements, etc.), and one per "level" (level-specific textures and geometry). That way, you just dispose the level-specific content manager and start a new one up for a new level. This pattern just screams RAII to me. The gotcha is that each ContentManager can house multiple types.
Now at least at the moment, I'm at a loss for how to do this in a type-safe, RAII friendly manner in C++. It's easy if I create a ContentManager per type of content to load into it (ContentManager<Texture2D>, ContentManager<LevelData>, ContentManager<BlahBlahBlah>, etc.), but then I lose the ease of working with a single object. I could add a strongly typed reference for each type into ContentManager (ContentManager.LoadTexture2D, ContentManager.LoadLevelData, etc.), but then I lose loose coupling. I could just keep a hash_map<string, void*> and force dynamic_cast to be used on load, but then I lose RAII cleanup of each subobject.
I'm sure there is a graceful solution to this that I just haven't found yet, but it's a fun problem space to explore.
Minor update: This weekend has taught me that I have much more to learn about the wonderful world of shared_ptr<T>. The last major C++ codebase I worked in was a barely C++'d C codebase, so my mental model of C++ is still skewed horribly.
XNA's ContentManager has a ton of features that seem like they'd work wonderfully in C++'s RAII world. When you call .Load<T>, it first checks to see if the content has already been loaded through this particular ContentManager. If it has, you get the cached copy back. Otherwise, it tries to load the file, instantiates the proper type loader, caches the loaded object, then returns a reference to the object. When ContentManager is disposed of, it handles cleaning up all of its cached objects for you. Plus, I can keep multiple ContentManagers around: one for "common" items (characters, UX elements, etc.), and one per "level" (level-specific textures and geometry). That way, you just dispose the level-specific content manager and start a new one up for a new level. This pattern just screams RAII to me. The gotcha is that each ContentManager can house multiple types.
Now at least at the moment, I'm at a loss for how to do this in a type-safe, RAII friendly manner in C++. It's easy if I create a ContentManager per type of content to load into it (ContentManager<Texture2D>, ContentManager<LevelData>, ContentManager<BlahBlahBlah>, etc.), but then I lose the ease of working with a single object. I could add a strongly typed reference for each type into ContentManager (ContentManager.LoadTexture2D, ContentManager.LoadLevelData, etc.), but then I lose loose coupling. I could just keep a hash_map<string, void*> and force dynamic_cast to be used on load, but then I lose RAII cleanup of each subobject.
I'm sure there is a graceful solution to this that I just haven't found yet, but it's a fun problem space to explore.
Minor update: This weekend has taught me that I have much more to learn about the wonderful world of shared_ptr<T>. The last major C++ codebase I worked in was a barely C++'d C codebase, so my mental model of C++ is still skewed horribly.
December 17, 2013
Stack Overflow
December 3, 2013
In Boston This Week
Don't expect any source or blog updates this week. I'm in Boston for work until Saturday.
November 30, 2013
FIX: Missing { in modes text file
Problem:
You are working on script mods for Left 4 Dead 2 and when you are loading your mod, you keep getting an error about a missing open curly brace. You've checked the file and everything appears to be lining up.
Solution:
Valve does a lot of things right with the Source engine. Handling byte-order markers at the beginning of scripts is not one of them. Open a command prompt, browse to your script, and use the "more" command to output your script to the command line. What you are looking for is three garbage characters at the beginning of your script.
If you see those garbage bytes, congratulations, you've got an easy problem to solve.
Download and install Notepad++. Open your script. Go to the Encoding menu. Change the encoding to either "Encode in ANSI" or "Encode in UTF-8 without BOM." Save your script.
You are working on script mods for Left 4 Dead 2 and when you are loading your mod, you keep getting an error about a missing open curly brace. You've checked the file and everything appears to be lining up.
Solution:
Valve does a lot of things right with the Source engine. Handling byte-order markers at the beginning of scripts is not one of them. Open a command prompt, browse to your script, and use the "more" command to output your script to the command line. What you are looking for is three garbage characters at the beginning of your script.
If you see those garbage bytes, congratulations, you've got an easy problem to solve.
Download and install Notepad++. Open your script. Go to the Encoding menu. Change the encoding to either "Encode in ANSI" or "Encode in UTF-8 without BOM." Save your script.
November 29, 2013
Games and Tools on Steam using .NET
Some of you may know that I have a near unhealthy Steam library, and I decided to spend a little time trying to figure out how many games on Steam are using the .NET Framework. I know most games are developed using native C/C++, and thanks to the popularity of Minecraft there's a growing Java contingent (like Delver and 3079), but I'll always have a soft spot for .NET.
This list is only games that use .NET in some respect. Some games install .NET but don't appear to use it (like Batman: Arkham Origins). Unity games are called out if they use Mono either instead of or in addition to native code libraries for logic.
This list isn't definitive, so if you know of other games on Steam that are using .NET or if I've put something on this list that shouldn't be here, please let me know by posting in the comments.
Title of Game (Game Framework/Technology)
[Update 1/16] Added GunCraft and Axis Game Factory.
This list is only games that use .NET in some respect. Some games install .NET but don't appear to use it (like Batman: Arkham Origins). Unity games are called out if they use Mono either instead of or in addition to native code libraries for logic.
This list isn't definitive, so if you know of other games on Steam that are using .NET or if I've put something on this list that shouldn't be here, please let me know by posting in the comments.
Title of Game (Game Framework/Technology)
- Adventures of Shuggy (XNA 4.0)
- AI War: Fleet Command (Unity)
- Ancients of Ooga (Windows Forms for settings app only)
- Apogee Throwback Pack (WPF for launcher only)
- Atom Zombie Smasher (Tao.OpenGL)
- Bastion (XNA 3.1)
- Borderlands 2 (WPF for launcher only. Has XNA 4.0 linked, but doesn't appear to use it)
- Breath of Death VII (XNA 4.0)
- Cthulhu Saves the World (XNA 4.0)
- Cubemen (Unity)
- Cubemen 2 (Unity)
- Dead Pixels (XNA 4.0)
- DLC Quest (XNA 4.0)
- Dust: An Elysian Tail (XNA 4.0)
- Fable 3 (SlimDX for video options applet only)
- Far Cry 3 (Windows Forms for Editor)
- FEZ (MonoGame)
- GunCraft (XNA? Need to validate)
- Hammerwatch (OpenTK)
- Lucidity (XNA 3.1)
- Magicka (XNA 3.1)
- Outlast (Unreal Engine...appears to just be C++/CLI managed hooks for debugging and editor wireup)
- Penny Arcade's On The Rain-Slick Precipice of Darkness, Episode 3 (XNA 4.0)
- Penny Arcade's On The Rain-Slick Precipice of Darkness, Episode 4 (XNA 4.0)
- Rogue Legacy (XNA 4.0)
- Sol Survivor (XNA 3.1)
- Space Engineers (SharpDX)
- Space Hulk (Unity)
- Terraria (XNA 4.0)
- The Witcher Enhanced Edition Digital Comic (Unity)
- The Witcher 2 (C++/CLI, COM interop to handle registration, password recovery, etc.)
- Axis Game Factory (Unity)
- GameMaker (Asset compiler, development web server)
- Music Creator 6 Touch (Fault reporter)
[Update 1/16] Added GunCraft and Axis Game Factory.
November 20, 2013
RomTerraria 3, RTMGRewriter Source Release
I've dropped the source for RomTerraria 3 and RTMGRewriter at my new site:
http://romsteadygames.us/
License TBD. Please don't hotlink the files.
http://romsteadygames.us/
License TBD. Please don't hotlink the files.
RomTerraria.MonoGame Linux Update
This will all make sense once the source release is made later today...
Tested on Ubuntu 12.04 LTS
Install Mono:
sudo apt-get install mono-complete
Install OpenAL:
sudo apt-get install libopenal1 libopenal-dev
Install SDL mixer:
sudo apt-get install sdl-mixer1.2
Rename (Fixing the casing as part of the conversion process for release):
Content/Images/ITem_893 to Item_893
Content/Images/ITem_951 to Item_951
Content/Images/Bone_Eyes to Bone_eyes
Run via:
mono ./TerrariaServer.Linux.exe
mono ./Terraria.Linux.exe
Creates files under ~/My Games/Terraria
TerrariaServer.Linux.exe: Ran fine.
Terraria.Linux.exe: Entire window shifted out of position.
Tested on Ubuntu 12.04 LTS
Install Mono:
sudo apt-get install mono-complete
Install OpenAL:
sudo apt-get install libopenal1 libopenal-dev
Install SDL mixer:
sudo apt-get install sdl-mixer1.2
Rename (Fixing the casing as part of the conversion process for release):
Content/Images/ITem_893 to Item_893
Content/Images/ITem_951 to Item_951
Content/Images/Bone_Eyes to Bone_eyes
Run via:
mono ./TerrariaServer.Linux.exe
mono ./Terraria.Linux.exe
Creates files under ~/My Games/Terraria
TerrariaServer.Linux.exe: Ran fine.
Terraria.Linux.exe: Entire window shifted out of position.
RomTerraria Source Release, RomTerraria.MonoGame Release Incoming
I'll be releasing the currently shipping source code for RomTerraria 3.0 Preview Release tonight. It'll be a Visual Studio 2013 solution, but you should be able to use Visual Studio 2012 or even Visual Studio 2010 to work with it.
I'll also be releasing the source for a side project...RomTerraria.MonoGame. Essentially, I've been working on creating a one-click solution to convert compiled XNA projects to MonoGame projects. It's not as easy as I'd like it to be, but I've been making great progress using Terraria as a test bed and hopefully people with more time than me can help make it a reality.
I've written code that injects the proper set of MonoMac calls necessary for Mac games in MonoGame to work right and changes the entry point to the assembly...
I've written code that either removes unneeded Win32 calls or replaces them with stubs, and more.
I'll also be releasing the source for a side project...RomTerraria.MonoGame. Essentially, I've been working on creating a one-click solution to convert compiled XNA projects to MonoGame projects. It's not as easy as I'd like it to be, but I've been making great progress using Terraria as a test bed and hopefully people with more time than me can help make it a reality.
I've written code that injects the proper set of MonoMac calls necessary for Mac games in MonoGame to work right and changes the entry point to the assembly...
I've written code that either removes unneeded Win32 calls or replaces them with stubs, and more.
November 14, 2013
Hack: Find Terraria Folder
Just a quick hack to share. This is the code I'm using to find your Terraria installation in RomTerraria 3.0.
static string GamePath = "";
static List<string> FindDetails = new List<string>();
static List<Process> GetProcessesByName(string machine, string filter, RegexOptions options)
{
List<Process> processList = new List<Process>();
// Get the current processes
Process[] runningProcesses = Process.GetProcesses(machine);
// Find those that match the specified regular expression
Regex processFilter = new Regex(filter, options);
foreach (Process current in runningProcesses)
{
// Check for a match.
if (processFilter.IsMatch(current.ProcessName))
{
processList.Add(current);
}
// Dispose of any we're not keeping
else current.Dispose();
}
// Return the filtered list as an array
return processList;
}
static bool FindGame()
{
var steamCandidates = GetProcessesByName(".", "steam", RegexOptions.IgnoreCase);
string steamFolder = String.Empty;
foreach (var p in steamCandidates)
{
try
{
if (p.MainModule.ModuleName.ToLower() == "steam.exe")
{
FindDetails.Add("Steam.exe process found.");
FileInfo f = new FileInfo(p.MainModule.FileName);
steamFolder = f.DirectoryName;
var d = f.Directory.GetDirectories("steamapps");
if (d.Count() > 0)
{
FindDetails.Add("Steamapps folder found.");
d = d[0].GetDirectories("common");
if (d.Count() > 0)
{
FindDetails.Add("Common folder found.");
d = d[0].GetDirectories("terraria");
if (d.Count() > 0)
{
FindDetails.Add("Terraria folder found.");
GamePath = d[0].FullName;
return true;
}
}
}
}
}
catch (System.ComponentModel.Win32Exception)
{
// Ignore this...means we were looking at SteamService.
}
}
if (FindDetails.Count == 0)
{
FindDetails.Add("Could not find a process named Steam.exe running.");
return false;
}
// Last chance: let's operate under the assumption that Terraria is in a different Steam install folder
string steamConfig = Path.Combine(steamFolder, @"config/config.vdf");
FindDetails.Add(String.Format("Checking Steam config at {0}", steamConfig));
GamePath = ParseConfig(steamConfig, @"InstallConfigStore/Software/Valve/Steam/apps/105600/installdir");
return !String.IsNullOrWhiteSpace(GamePath);
}
private static string ParseConfig(string steamConfig, string configNode)
{
Stack<string> keys = new Stack<string>();
string lastString = String.Empty;
using (StreamReader sr = new StreamReader(steamConfig))
{
while (!sr.EndOfStream)
{
string s = sr.ReadLine().Trim();
if (s.StartsWith("{"))
{
keys.Push(lastString);
}
else if (s.StartsWith("}"))
{
keys.Pop();
}
else if (s.StartsWith("\""))
{
var tokens = s.Split('\t').Where(a => !String.IsNullOrWhiteSpace(a)).ToArray();
lastString = tokens[0].Trim('\"');
if (tokens.Length > 1)
{
string key = String.Join("/", keys.ToArray().Reverse()) + "/" + lastString;
Debug.WriteLine(key, "ParseConfig");
if (key.Equals(configNode, StringComparison.InvariantCultureIgnoreCase))
{
return tokens[1].Trim('\"').Replace(@"\\", @"\");
}
}
}
}
}
return null;
}
static string GamePath = "";
static List<string> FindDetails = new List<string>();
static List<Process> GetProcessesByName(string machine, string filter, RegexOptions options)
{
List<Process> processList = new List<Process>();
// Get the current processes
Process[] runningProcesses = Process.GetProcesses(machine);
// Find those that match the specified regular expression
Regex processFilter = new Regex(filter, options);
foreach (Process current in runningProcesses)
{
// Check for a match.
if (processFilter.IsMatch(current.ProcessName))
{
processList.Add(current);
}
// Dispose of any we're not keeping
else current.Dispose();
}
// Return the filtered list as an array
return processList;
}
static bool FindGame()
{
var steamCandidates = GetProcessesByName(".", "steam", RegexOptions.IgnoreCase);
string steamFolder = String.Empty;
foreach (var p in steamCandidates)
{
try
{
if (p.MainModule.ModuleName.ToLower() == "steam.exe")
{
FindDetails.Add("Steam.exe process found.");
FileInfo f = new FileInfo(p.MainModule.FileName);
steamFolder = f.DirectoryName;
var d = f.Directory.GetDirectories("steamapps");
if (d.Count() > 0)
{
FindDetails.Add("Steamapps folder found.");
d = d[0].GetDirectories("common");
if (d.Count() > 0)
{
FindDetails.Add("Common folder found.");
d = d[0].GetDirectories("terraria");
if (d.Count() > 0)
{
FindDetails.Add("Terraria folder found.");
GamePath = d[0].FullName;
return true;
}
}
}
}
}
catch (System.ComponentModel.Win32Exception)
{
// Ignore this...means we were looking at SteamService.
}
}
if (FindDetails.Count == 0)
{
FindDetails.Add("Could not find a process named Steam.exe running.");
return false;
}
// Last chance: let's operate under the assumption that Terraria is in a different Steam install folder
string steamConfig = Path.Combine(steamFolder, @"config/config.vdf");
FindDetails.Add(String.Format("Checking Steam config at {0}", steamConfig));
GamePath = ParseConfig(steamConfig, @"InstallConfigStore/Software/Valve/Steam/apps/105600/installdir");
return !String.IsNullOrWhiteSpace(GamePath);
}
private static string ParseConfig(string steamConfig, string configNode)
{
Stack<string> keys = new Stack<string>();
string lastString = String.Empty;
using (StreamReader sr = new StreamReader(steamConfig))
{
while (!sr.EndOfStream)
{
string s = sr.ReadLine().Trim();
if (s.StartsWith("{"))
{
keys.Push(lastString);
}
else if (s.StartsWith("}"))
{
keys.Pop();
}
else if (s.StartsWith("\""))
{
var tokens = s.Split('\t').Where(a => !String.IsNullOrWhiteSpace(a)).ToArray();
lastString = tokens[0].Trim('\"');
if (tokens.Length > 1)
{
string key = String.Join("/", keys.ToArray().Reverse()) + "/" + lastString;
Debug.WriteLine(key, "ParseConfig");
if (key.Equals(configNode, StringComparison.InvariantCultureIgnoreCase))
{
return tokens[1].Trim('\"').Replace(@"\\", @"\");
}
}
}
}
}
return null;
}
November 11, 2013
XNA Hack: Create RenderTarget2D greater than 4096x4096
Problem:
XNA's HiDef profile has a maximum Texture2D size of 4096x4096. RenderTarget2D has a backing Texture2D object, and this limitation is enforced at time of construction.
Solution:
Time to get a bit tricky with reflection. Limitations in XNA are all stored in Microsoft.Xna.Framework.Graphics.ProfileCapabilities, an internal class. Fortunately, they aren't constants...
In your Initialize() function...
Replace MaxTextureSize in the last line with whatever you feel your maximum size should be. Ideally, you'll be querying DirectX 9 and setting the value to the maximum value allowed by D3D9Caps.
XNA's HiDef profile has a maximum Texture2D size of 4096x4096. RenderTarget2D has a backing Texture2D object, and this limitation is enforced at time of construction.
Solution:
Time to get a bit tricky with reflection. Limitations in XNA are all stored in Microsoft.Xna.Framework.Graphics.ProfileCapabilities, an internal class. Fortunately, they aren't constants...
In your Initialize() function...
Assembly xna = Assembly.GetAssembly(typeof(GraphicsProfile)); Type profileCapabilities = xna.GetType("Microsoft.Xna.Framework.Graphics.ProfileCapabilities", true); if (profileCapabilities != null) { FieldInfo maxTextureSize = profileCapabilities.GetField("MaxTextureSize", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo hidefProfile = profileCapabilities.GetField("HiDef", BindingFlags.Static | BindingFlags.NonPublic); if (maxTextureSize != null && hidefProfile != null) { object profile = hidefProfile.GetValue(null); maxTextureSize.SetValue(hidefProfile.GetValue(null), MaxTextureSize); } }
Replace MaxTextureSize in the last line with whatever you feel your maximum size should be. Ideally, you'll be querying DirectX 9 and setting the value to the maximum value allowed by D3D9Caps.
November 9, 2013
Mono.Cecil Lessons Learned
Well, working on RomTerraria 3 has been fun. I got to learn more about MSIL than I ever wanted to, and got to work with a great tool.
A few lessons I learned working with Mono.Cecil that might help others.
EmbeddedResources. If you use the System.IO.Stream overload for an EmbeddedResource, it won't try to open the stream until you save out the assembly, so keep scope and closing of your streams in mind.
Always Import Your Methods. If you are calling another method in your calls, ALWAYS import. If the method already exists, no harm. If it doesn't exist, you'll save yourself some hassle.
var nextInstruction = processor.Create(Mono.Cecil.Cil.OpCodes.Call, method.Module.Import(hookMethod));
processor.InsertAfter(newInstruction, nextInstruction);
Replacing default values. I had to replace three types of default values in methods: strings, bools, and ints. I ended up creating three different helpers for these.
public static void ReplaceStringInMethod(MethodDefinition method, string oldString, string newString)
{
for (int i = 0; i < method.Body.Instructions.Count; i++)
{
if (method.Body.Instructions[i].OpCode == Mono.Cecil.Cil.OpCodes.Ldstr &&
method.Body.Instructions[i].Operand.ToString() == oldString)
{
method.Body.Instructions[i].Operand = newString;
}
}
}
public static void ChangeDefaultBooleanValue(MethodDefinition method, string fieldName, bool newValue)
{
var il = method.Body.GetILProcessor();
foreach (var instruction in il.Body.Instructions)
{
if (instruction.OpCode == Mono.Cecil.Cil.OpCodes.Stsfld)
{
var field = (FieldDefinition)instruction.Operand;
if (field.FullName == fieldName)
{
var previnst = instruction.Previous;
if (previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4_1 ||
previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4_0)
{
previnst.OpCode = newValue ? Mono.Cecil.Cil.OpCodes.Ldc_I4_1 : Mono.Cecil.Cil.OpCodes.Ldc_I4_0;
return;
}
}
}
}
throw new KeyNotFoundException(String.Format("Default value not found for '{0}'.", newValue));
}
public static void ChangeDefaultInt32Value(MethodDefinition method, string fieldName, int newValue)
{
var il = method.Body.GetILProcessor();
foreach (var instruction in il.Body.Instructions)
{
if (instruction.OpCode == Mono.Cecil.Cil.OpCodes.Stsfld)
{
var field = (FieldDefinition)instruction.Operand;
if (field.FullName == fieldName)
{
var previnst = instruction.Previous;
if (previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4)
{
previnst.Operand = newValue;
return;
}
}
}
}
throw new KeyNotFoundException(String.Format("Default value not found for '{0}'.", newValue));
}
There's still a lot of stuff I need to learn about Mono.Cecil. I'm working on making my code injection routines generic and I'm cleaning up a lot of code before I release the code to RomTerraria, but this is an extremely powerful tool for greybox code modification and should be in every .NET developer's toolbox.
A few lessons I learned working with Mono.Cecil that might help others.
EmbeddedResources. If you use the System.IO.Stream overload for an EmbeddedResource, it won't try to open the stream until you save out the assembly, so keep scope and closing of your streams in mind.
Always Import Your Methods. If you are calling another method in your calls, ALWAYS import. If the method already exists, no harm. If it doesn't exist, you'll save yourself some hassle.
var nextInstruction = processor.Create(Mono.Cecil.Cil.OpCodes.Call, method.Module.Import(hookMethod));
processor.InsertAfter(newInstruction, nextInstruction);
Replacing default values. I had to replace three types of default values in methods: strings, bools, and ints. I ended up creating three different helpers for these.
public static void ReplaceStringInMethod(MethodDefinition method, string oldString, string newString)
{
for (int i = 0; i < method.Body.Instructions.Count; i++)
{
if (method.Body.Instructions[i].OpCode == Mono.Cecil.Cil.OpCodes.Ldstr &&
method.Body.Instructions[i].Operand.ToString() == oldString)
{
method.Body.Instructions[i].Operand = newString;
}
}
}
public static void ChangeDefaultBooleanValue(MethodDefinition method, string fieldName, bool newValue)
{
var il = method.Body.GetILProcessor();
foreach (var instruction in il.Body.Instructions)
{
if (instruction.OpCode == Mono.Cecil.Cil.OpCodes.Stsfld)
{
var field = (FieldDefinition)instruction.Operand;
if (field.FullName == fieldName)
{
var previnst = instruction.Previous;
if (previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4_1 ||
previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4_0)
{
previnst.OpCode = newValue ? Mono.Cecil.Cil.OpCodes.Ldc_I4_1 : Mono.Cecil.Cil.OpCodes.Ldc_I4_0;
return;
}
}
}
}
throw new KeyNotFoundException(String.Format("Default value not found for '{0}'.", newValue));
}
public static void ChangeDefaultInt32Value(MethodDefinition method, string fieldName, int newValue)
{
var il = method.Body.GetILProcessor();
foreach (var instruction in il.Body.Instructions)
{
if (instruction.OpCode == Mono.Cecil.Cil.OpCodes.Stsfld)
{
var field = (FieldDefinition)instruction.Operand;
if (field.FullName == fieldName)
{
var previnst = instruction.Previous;
if (previnst.OpCode == Mono.Cecil.Cil.OpCodes.Ldc_I4)
{
previnst.Operand = newValue;
return;
}
}
}
}
throw new KeyNotFoundException(String.Format("Default value not found for '{0}'.", newValue));
}
There's still a lot of stuff I need to learn about Mono.Cecil. I'm working on making my code injection routines generic and I'm cleaning up a lot of code before I release the code to RomTerraria, but this is an extremely powerful tool for greybox code modification and should be in every .NET developer's toolbox.
November 7, 2013
RomTerraria 3 Preview Release Here
[Update 6/30/2015 7:09pm] Updated v4 version here for use with Steam and GOG 1.3.x and above.
I've released RomTerraria 3 as a preview release. This release should ONLY be used by people who want to play at resolutions greater than 2048 in either direction (up to 8192).
Features in this release:
- RomTerraria generates a new executable in your <My Documents>/My Games/Terraria named Terraria.Rewritten.exe. This executable is custom created based on the settings you select.
- RomTerraria's settings file is saved to <My Documents>/My Games/Terraria/config.rt so it won't impact your settings from stock Terraria.
- Force XNA into HiDef profile instead of Reach so that it will take advantage of Shader Model 3.0 spritebatches.
- Eliminates the depth buffer on render targets, reducing video card memory usage by half.
- Enhanced lighting (and its accompanying performance increases) will work at resolutions over 1920x1080 (up to 8192x8192). I found a workaround for XNA's RenderTarget2D size limitations.
Possible issues:
- If your system doesn't meet the HiDef profile, you'll fail miserably.
- May not work on the Collector's Edition.
[Update 1/19/2015 9:10p] Starting to add back in single player functions, like "Always Daylight" and "Spawn NPCs Fast."
v0.99.1 (deprecated) download here.
Unzip this to someplace OTHER than your Terraria install folder.
Screenshots
Note: If you encounter any crashes, hit Ctrl-C in the dialog that pops up with the stack trace, and paste the stack trace into a reply to this post.
Note #2: I can't accept donations or any sort of financial compensation for RomTerraria. If you want to show some support for RomTerraria, please donate to support survivors of Typhoon Haiyan.
Note #3: If you are getting an error that says "Unable to find Terraria," make sure Steam is running. If it still can't find Terraria, uninstall and reinstall the game. I rely on metadata that Steam provides to find Terraria in alternate install locations. If it still can't find the game, add me as a friend on Steam and I'll see what I can find.
[Update 11/12 7:35p] v0.96 released. No bug fixes, but pops up a prompt if you drop RomTerraria 3 in the same folder as Terraria to prevent an error.
[Update 11/13 10:56a] Added donation link for survivors of Typhoon Haiyan, cleaned up the page.
[Update 11/29 4:15p] v0.97 released. Additional code for people who are having problems patching.
[Update 12/19 10:27p] This works with no modification on the new v1.2.2 release. If you had previously patched v1.2.1, just rerun the patcher and it will update Terraria.Rewritten.exe for you.
[Update 2/19/2014 10:12p] This works with no modification on the new v1.2.3.1 release. If you had previously patched v1.2.1, just rerun the patcher and it will update Terraria.Rewritten.exe for you.
[Update 1/18/2015 11:45a] This version now works with the GOG.com version and puts no files in the Terraria folder.
[Update 1/19/2015 9:10p] This version is starting to add in single player hooks.
[Update 2/3/2015] Cleaning up some ancient comments so relevant topics bubble up.
I've released RomTerraria 3 as a preview release. This release should ONLY be used by people who want to play at resolutions greater than 2048 in either direction (up to 8192).
Features in this release:
- RomTerraria generates a new executable in your <My Documents>/My Games/Terraria named Terraria.Rewritten.exe. This executable is custom created based on the settings you select.
- RomTerraria's settings file is saved to <My Documents>/My Games/Terraria/config.rt so it won't impact your settings from stock Terraria.
- Force XNA into HiDef profile instead of Reach so that it will take advantage of Shader Model 3.0 spritebatches.
- Eliminates the depth buffer on render targets, reducing video card memory usage by half.
- Enhanced lighting (and its accompanying performance increases) will work at resolutions over 1920x1080 (up to 8192x8192). I found a workaround for XNA's RenderTarget2D size limitations.
Possible issues:
- If your system doesn't meet the HiDef profile, you'll fail miserably.
- May not work on the Collector's Edition.
[Update 1/19/2015 9:10p] Starting to add back in single player functions, like "Always Daylight" and "Spawn NPCs Fast."
v0.99.1 (deprecated) download here.
Unzip this to someplace OTHER than your Terraria install folder.
Screenshots
Note: If you encounter any crashes, hit Ctrl-C in the dialog that pops up with the stack trace, and paste the stack trace into a reply to this post.
Note #2: I can't accept donations or any sort of financial compensation for RomTerraria. If you want to show some support for RomTerraria, please donate to support survivors of Typhoon Haiyan.
Note #3: If you are getting an error that says "Unable to find Terraria," make sure Steam is running. If it still can't find Terraria, uninstall and reinstall the game. I rely on metadata that Steam provides to find Terraria in alternate install locations. If it still can't find the game, add me as a friend on Steam and I'll see what I can find.
[Update 11/12 7:35p] v0.96 released. No bug fixes, but pops up a prompt if you drop RomTerraria 3 in the same folder as Terraria to prevent an error.
[Update 11/13 10:56a] Added donation link for survivors of Typhoon Haiyan, cleaned up the page.
[Update 11/29 4:15p] v0.97 released. Additional code for people who are having problems patching.
[Update 12/19 10:27p] This works with no modification on the new v1.2.2 release. If you had previously patched v1.2.1, just rerun the patcher and it will update Terraria.Rewritten.exe for you.
[Update 2/19/2014 10:12p] This works with no modification on the new v1.2.3.1 release. If you had previously patched v1.2.1, just rerun the patcher and it will update Terraria.Rewritten.exe for you.
[Update 1/18/2015 11:45a] This version now works with the GOG.com version and puts no files in the Terraria folder.
[Update 1/19/2015 9:10p] This version is starting to add in single player hooks.
[Update 2/3/2015] Cleaning up some ancient comments so relevant topics bubble up.
November 4, 2013
Another RomTerraria Delay
Sorry, guys. I've got one last exception throwing that I have to solve before I can release this.
No ETA, since the exception that's being thrown is telling me that something is going wrong the frame before.
[Update 11/5] Okay, I figured it out. There are some corner cases where Terraria will dispose of its render targets, not recreate them, but still have renderToScreen set to false, which leads to XNA calls telling Terraria to render to a disposed render target.
I'll spend tonight analyzing the code base and hopefully I'll be able to figure out not only which MSIL instructions to inject, but where...
[Update #2 11/5] Easier to do this fix in C# and then inject the method call...
[Update #3 11/5] New executable sent out to my testers. Final list of changes I needed to do include:
- Hook into Terraria.Main.Window.ClientSizeChanged and reset the graphics device and default render target.
- Completely replace Terraria.Main.InitTargets() and Terraria.Main.ReleaseTargets().
- Clear the "loaded" flag on all of the content loaded outside of Terraria.Main.LoadContent() to prevent ObjectDisposedExceptions.
[Update #4 11/6] A version of RomTerraria with only XNA Hi-Def and large resolution support will be released as a preview as soon as I hear back from all my testers. Please be aware that because of how MSIL injection works, RomTerraria will ONLY work with the Steam version of Terraria at the moment. Once I get my hands on a retail copy, I'll look into extending it to work properly with the retail version.
[Update #5 11/6] Working on a weird issue where the XNA profile is resetting back to Reach, but almost there. Tossing in one extra feature to make up for the delay. Guess what it is? Hint: what was exclusive to the retail release?
No ETA, since the exception that's being thrown is telling me that something is going wrong the frame before.
[Update 11/5] Okay, I figured it out. There are some corner cases where Terraria will dispose of its render targets, not recreate them, but still have renderToScreen set to false, which leads to XNA calls telling Terraria to render to a disposed render target.
I'll spend tonight analyzing the code base and hopefully I'll be able to figure out not only which MSIL instructions to inject, but where...
[Update #2 11/5] Easier to do this fix in C# and then inject the method call...
- Hook into Terraria.Main.Window.ClientSizeChanged and reset the graphics device and default render target.
- Completely replace Terraria.Main.InitTargets() and Terraria.Main.ReleaseTargets().
- Clear the "loaded" flag on all of the content loaded outside of Terraria.Main.LoadContent() to prevent ObjectDisposedExceptions.
[Update #4 11/6] A version of RomTerraria with only XNA Hi-Def and large resolution support will be released as a preview as soon as I hear back from all my testers. Please be aware that because of how MSIL injection works, RomTerraria will ONLY work with the Steam version of Terraria at the moment. Once I get my hands on a retail copy, I'll look into extending it to work properly with the retail version.
[Update #5 11/6] Working on a weird issue where the XNA profile is resetting back to Reach, but almost there. Tossing in one extra feature to make up for the delay. Guess what it is? Hint: what was exclusive to the retail release?
November 3, 2013
RomTerraria 3.0 Feature #1: Large Resolution Fixes
The most common complaint about RomTerraria lately has been that since Terraria 1.2 was released, Eyefinity/nVidia Surround support breaks down when either horizontal or vertical resolution goes above 2048 pixels when using default lighting.
The reason? They moved to a render-to-texture system and fixed the maximum render-to-texture size at 2048x2048.
Well, with RomTerraria 3.0, you'll have access to resolutions up to
Here's how it will work. The code is split into three chunks. Two chunks will ship as an accompanying .NET assembly, RTHooks.dll. The third chunk is actually rewritten into the Terraria executable.
Lines 54-56 change the maximum resolution from 1920x1080 to
Lines 60-75 wire up calls to RTHooks inside Terraria.Main.Initialize(), similar to what I did with a subclass back with RomTerraria 1 and 2, but without requiring a subclass. These two hooks set the game to run at the current desktop resolution and enable cooperative full screen rendering.
Lines 78-87 walk through Terraria.Main.InitTargets() and finds the constants there. There are only a couple of constants, and they all are tied to calculating the size of the render targets. This boosts them up to
Currently looking at a November
[Update 11/6] Screenshots! Thanks, Steve!
[Update #2 11/6] Sorry, but due to an XNA 4.0 limitation on the maximum size of a render target, you will be limited to 4096x4096.
November 2, 2013
Mono.Cecil: Change Base Class, Add Field
I wanted to swap out a base class in Terraria with another base class that exists in a different DLL, and wasn't quite sure how to do it, so I created a simple test project for experimenting with Mono.Cecil.
You can download the code for my experiments here.
The sample code has three projects: RewriteMe, InjectMe, and MonoCecilExperiments. RewriteMe has a class called ReplaceMyBaseClass and I want to swap out its base class with a new base class in InjectMe. The code should be documented well enough that people should be able to follow along.
Essentially, I want to work at the IL level as little as possible. If I can inject a call to some C# code, all the better.
You can download the code for my experiments here.
The sample code has three projects: RewriteMe, InjectMe, and MonoCecilExperiments. RewriteMe has a class called ReplaceMyBaseClass and I want to swap out its base class with a new base class in InjectMe. The code should be documented well enough that people should be able to follow along.
Essentially, I want to work at the IL level as little as possible. If I can inject a call to some C# code, all the better.
November 1, 2013
No RomTerraria 3 Release Today
The good news: I've got hook injection working throughout the project using Mono.Cecil, and none of my code had to change after 1.2.1.2 was released. In other words, once I'm done getting this right, should be able to keep working with minimum or no code changes going forward.
The bad news: It's taking longer than I'd hoped to get this ready, and being sick for a day didn't help.
I'll try to keep you posted.
The bad news: It's taking longer than I'd hoped to get this ready, and being sick for a day didn't help.
I'll try to keep you posted.
October 31, 2013
Shawn Guce: Take Two
I read an article on Kotaku today about a pirate who sounded a little familiar.
Back in 2008, Activision went after some pirates. Shawn Guce settled out of court for $100,000, and then went out trying to expunge records of this settlement from the net.
Some people never learn...
Back in 2008, Activision went after some pirates. Shawn Guce settled out of court for $100,000, and then went out trying to expunge records of this settlement from the net.
Some people never learn...
October 27, 2013
Coders: TerrariaServer without XNA?
I think it's possible to get TerrariaServer running without XNA without rewriting the code, which would be the first step in getting Terraria servers able to be hosted on Linux.
I was using Mono.Cecil to try to modify Terraria on the fly. My hope was to make it so that I could inject hooks directly into the Terraria executable to allow things like replacement UI layers or modifying certain constant values, and while I was doing it, I found that I could replace all of the XNA assembly references with references to an assembly under my control.
I was then able to write stubs and mocks for all of the XNA objects needed on the critical path and get it to the point where Terraria wouldn't crash on connect. It won't accept the connection, but it isn't crashing.
Even if this doesn't get out to Linux, there would be some extremely minor benefits getting this going anyway. The mocked version uses 6MB less memory and doesn't require an XNA-compatible video card on the server.
Now, there's nothing here that's usable by a standard end user at the moment, but here's a source release for people who are interested in this problem space and want to take it and run with it.
http://www.romsteady.net/Projects/DasmAsm-20131027.zip (VS2010 solution)
I was using Mono.Cecil to try to modify Terraria on the fly. My hope was to make it so that I could inject hooks directly into the Terraria executable to allow things like replacement UI layers or modifying certain constant values, and while I was doing it, I found that I could replace all of the XNA assembly references with references to an assembly under my control.
I was then able to write stubs and mocks for all of the XNA objects needed on the critical path and get it to the point where Terraria wouldn't crash on connect. It won't accept the connection, but it isn't crashing.
Even if this doesn't get out to Linux, there would be some extremely minor benefits getting this going anyway. The mocked version uses 6MB less memory and doesn't require an XNA-compatible video card on the server.
Now, there's nothing here that's usable by a standard end user at the moment, but here's a source release for people who are interested in this problem space and want to take it and run with it.
http://www.romsteady.net/Projects/DasmAsm-20131027.zip (VS2010 solution)
October 26, 2013
Terraria Server on Linux?
This isn't directly RomTerraria related, but could be related to how RomTerraria is going to be working going forward.
The biggest problem with making a non-source mod like RomTerraria is that every time the game changes, even if your code wouldn't normally be affected, your app completely breaks because the binding changes. There's no way to ensure a consistent experience for your code.
With early versions of RomTerraria, I took advantage of the XNA component framework. That gave me a perfect place to inject my code where it would work nicely with all of the code that already exists in the game. However, with v1.2, the call to base.Draw() removed my ability to easily inject rendering calls for things like the minimap and HUD updates I had put in before. In addition, since Terraria uses some Win32 calls internally, you can't really use Terraria in a non-Windows environment.
What would work out well was if I could take an arbitrary Terraria executable and inject code without distributing Terraria's code. Fortunately, a tool set does exist for doing this: ildasm and ilasm. You run ildasm against Terraria.exe or TerrariaServer.exe and you get the MSIL for the executable. You apply your patches, then run ilasm to recreate your executable.
Example disassembly:
ildasm TerrariaServer.exe /out=TerrariaServer.il
Example reassembly:
ilasm /32bitpreferred /output:TerrariaServerRecompile.exe TerrariaServer.il
Then you can just run TerrariaServerRecompile.exe and have a round-tripped server executable.
So what I was thinking of doing was writing dummy stubs for most of the interop code (NATUPNPLib.UPnPNAT, etc.) and trying to get TerrariaServer.exe able to run on Linux as an experiment. If I can get this to work, I can then make the next step for RomTerraria: inject entry points directly into the Terraria executable without redistributing any Terraria code.
Thoughts?
The biggest problem with making a non-source mod like RomTerraria is that every time the game changes, even if your code wouldn't normally be affected, your app completely breaks because the binding changes. There's no way to ensure a consistent experience for your code.
With early versions of RomTerraria, I took advantage of the XNA component framework. That gave me a perfect place to inject my code where it would work nicely with all of the code that already exists in the game. However, with v1.2, the call to base.Draw() removed my ability to easily inject rendering calls for things like the minimap and HUD updates I had put in before. In addition, since Terraria uses some Win32 calls internally, you can't really use Terraria in a non-Windows environment.
What would work out well was if I could take an arbitrary Terraria executable and inject code without distributing Terraria's code. Fortunately, a tool set does exist for doing this: ildasm and ilasm. You run ildasm against Terraria.exe or TerrariaServer.exe and you get the MSIL for the executable. You apply your patches, then run ilasm to recreate your executable.
Example disassembly:
ildasm TerrariaServer.exe /out=TerrariaServer.il
Example reassembly:
ilasm /32bitpreferred /output:TerrariaServerRecompile.exe TerrariaServer.il
Then you can just run TerrariaServerRecompile.exe and have a round-tripped server executable.
So what I was thinking of doing was writing dummy stubs for most of the interop code (NATUPNPLib.UPnPNAT, etc.) and trying to get TerrariaServer.exe able to run on Linux as an experiment. If I can get this to work, I can then make the next step for RomTerraria: inject entry points directly into the Terraria executable without redistributing any Terraria code.
Thoughts?
October 14, 2013
No Updates This Week
I'm flying to San Jose/Cupertino/Sunnyvale tomorrow and won't be back until next week.
Work on RomTerraria will resume then.
Work on RomTerraria will resume then.
October 4, 2013
RomTerraria 3.0 Feature Request Post
[Update 11/9 1:25p] The preview release is over here.
Okay, after looking at the massive amount of change in Terraria 1.2.0.2, looks like I'm going to have to completely rewrite RomTerraria.
Functionality mods can still use the Component architecture in XNA, but visual mods can't due to changes made in how Terraria works.
So, consider this a complete startover. I'll be using no code from the previous versions of RomTerraria. If you have any requests for features to be added to RomTerraria 3.0, consider this your place to request them. I'll also add in updates on requests as they come in.
Request #1: Make it Linux compatible. That's not something I'll be able to do. Terraria is tightly bound to XNA and Windows at the moment. Redigit did say that he was looking at a port to MonoGame, but was having too many problems and Terraria 1 probably will never come to Linux. [11/3: I did manage to get the server running without XNA, which is promising.]
Request #2: Key re-binding. Already exists. Settings -> Controls
Request #3: Ponies. Thread for reference. Will bring back the pony.
Request #4: Save map as PNG. Okay, I can do this one. Should even be able to hotkey it. One key for a 1px-per-tile version, one key for a full-size version. [10/5: 50% done. 11/3: Complete, but unstable, so not ready for release.]
Request #5: Allow inventory to be moved. This would be really tricky, but not impossible. The only downside is that you wouldn't be able to use the default inventory key, unfortunately. I'll look at it for vNext.
Request #6: Have wings and rocket boots consume mana. Working on it.
Request #7: No shadows. Not sure I can do this one easily, but I'll see what I can do.
Request #8: Full map reveal. Should be easy.
Request #9: Invulnerability. Can't do that, but I might be able to make it so enemies die when they get within a certain range of you.
Request #10: Server support for mods. Will look into this one. [11/3: Doable. I've got code that will allow me to inject hooks anywhere...and I do mean ANYWHERE.]
Request #11: Smooth lighting and Eyefinity. What's wrong with it? I'm running an nVidia card on a single monitor, so I need clarification on this one so I know what I'm looking for. [11/3: Complete, released.]
Request #12: Add the bunny from the collector's edition for Steam copies without registry mucking. [11/6: Completed, but removed from the mod because it would result in the mod being banned from the official forums for the game.]
Okay, after looking at the massive amount of change in Terraria 1.2.0.2, looks like I'm going to have to completely rewrite RomTerraria.
Functionality mods can still use the Component architecture in XNA, but visual mods can't due to changes made in how Terraria works.
So, consider this a complete startover. I'll be using no code from the previous versions of RomTerraria. If you have any requests for features to be added to RomTerraria 3.0, consider this your place to request them. I'll also add in updates on requests as they come in.
Request #1: Make it Linux compatible. That's not something I'll be able to do. Terraria is tightly bound to XNA and Windows at the moment. Redigit did say that he was looking at a port to MonoGame, but was having too many problems and Terraria 1 probably will never come to Linux. [11/3: I did manage to get the server running without XNA, which is promising.]
Request #2: Key re-binding. Already exists. Settings -> Controls
Request #3: Ponies. Thread for reference. Will bring back the pony.
Request #4: Save map as PNG. Okay, I can do this one. Should even be able to hotkey it. One key for a 1px-per-tile version, one key for a full-size version. [10/5: 50% done. 11/3: Complete, but unstable, so not ready for release.]
Request #5: Allow inventory to be moved. This would be really tricky, but not impossible. The only downside is that you wouldn't be able to use the default inventory key, unfortunately. I'll look at it for vNext.
Request #6: Have wings and rocket boots consume mana. Working on it.
Request #7: No shadows. Not sure I can do this one easily, but I'll see what I can do.
Request #8: Full map reveal. Should be easy.
Request #9: Invulnerability. Can't do that, but I might be able to make it so enemies die when they get within a certain range of you.
Request #10: Server support for mods. Will look into this one. [11/3: Doable. I've got code that will allow me to inject hooks anywhere...and I do mean ANYWHERE.]
Request #11: Smooth lighting and Eyefinity. What's wrong with it? I'm running an nVidia card on a single monitor, so I need clarification on this one so I know what I'm looking for. [11/3: Complete, released.]
Request #12: Add the bunny from the collector's edition for Steam copies without registry mucking. [11/6: Completed, but removed from the mod because it would result in the mod being banned from the official forums for the game.]
September 9, 2013
State of the Nation
I've managed to go from a peak of almost 350 posts in a year to barely over a dozen in a year. I think some explanation is in order.
I've been working on several secret projects since joining Amazon in January 2012. One of the side effects of working on secret projects is that you aren't supposed to post about things that could potentially hint at what you are working on.
My hope was that I'd still be able to make lots of blog posts about games and other non-work-related items, but my available time each week for gaming has been minimal, and most of the really interesting stuff I've come across could, at least tangentially, be cross-referenced with enough other data points to reveal my project. The latter part really sucks because I've been discovering some amazing things and I have to wait until after my project is announced to share any of it.
Fortunately, my involvement with secret projects is starting to come to a conclusion, and I'm trying to figure what my options are.
Option one: Stay working on this secret project when it is no longer secret. Pros: I know this domain astoundingly well, and I'm on track to become a principal at Amazon within the next 12 months. Cons: As we get closer to release, the pressure is on to relax my quality bar, and I hate feeling pressured to enable what amounts to "intellectual masturbation" by allowing people to claim a milestone they didn't hit.
Option two: Stay at Amazon, but transition to another group. Pros: Amazon has internal game studios, and I could transition back into games and keep the Amazon-level salary. Cons: My participation in gaming outside of Amazon would be reduced significantly per the non-compete.
Option three: Leave Amazon. To be honest, I'm not even considering this option.
What will come of all this? I have no idea. My goal is to know for certain by my two-year Amazon anniversary.
I've been working on several secret projects since joining Amazon in January 2012. One of the side effects of working on secret projects is that you aren't supposed to post about things that could potentially hint at what you are working on.
My hope was that I'd still be able to make lots of blog posts about games and other non-work-related items, but my available time each week for gaming has been minimal, and most of the really interesting stuff I've come across could, at least tangentially, be cross-referenced with enough other data points to reveal my project. The latter part really sucks because I've been discovering some amazing things and I have to wait until after my project is announced to share any of it.
Fortunately, my involvement with secret projects is starting to come to a conclusion, and I'm trying to figure what my options are.
Option one: Stay working on this secret project when it is no longer secret. Pros: I know this domain astoundingly well, and I'm on track to become a principal at Amazon within the next 12 months. Cons: As we get closer to release, the pressure is on to relax my quality bar, and I hate feeling pressured to enable what amounts to "intellectual masturbation" by allowing people to claim a milestone they didn't hit.
Option two: Stay at Amazon, but transition to another group. Pros: Amazon has internal game studios, and I could transition back into games and keep the Amazon-level salary. Cons: My participation in gaming outside of Amazon would be reduced significantly per the non-compete.
Option three: Leave Amazon. To be honest, I'm not even considering this option.
What will come of all this? I have no idea. My goal is to know for certain by my two-year Amazon anniversary.
Profile Updated
Got an email this morning asking about a post from 2007, and realized that my profile had been out of date for almost two years.
Oops.
Oops.
June 8, 2013
RomTerraria and Game Development
Two small updates.
First, evidently, I'm supporting RomTerraria again. I've handled more support emails over the last week than I have in the last year. I may even be updating it for the 1.2 update, although it seems that I'll be removing features that are moving into the main program.
Second, I've been working on a game for BaconGameJam. I released a video of some of the tech I've built for it. It won't be done by the deadline, but I've got a good basis for a decent tile-based game and may have it complete shortly.
First, evidently, I'm supporting RomTerraria again. I've handled more support emails over the last week than I have in the last year. I may even be updating it for the 1.2 update, although it seems that I'll be removing features that are moving into the main program.
Second, I've been working on a game for BaconGameJam. I released a video of some of the tech I've built for it. It won't be done by the deadline, but I've got a good basis for a decent tile-based game and may have it complete shortly.
May 20, 2013
AngelHack 2013 Wrapup
This last weekend, I participated in AngelHack 2013.
I wasn't sure what to expect from AngelHack going into it. I'd found out about it on Thursday, and after much hemming, hawing, and gnashing of teeth, I decided to go for it.
I'm used to a GiveCamp-style model, where you've got your project well-defined in advance, but for this one I was able to decide for myself what I'd do within their restriction: your code must be completed within 24 hours.
I decided I'd use the opportunity to learn some "new-to-me" technology, specifically WebRTC's peer-to-peer data capabilities. However, I quickly found out that adoption isn't what I'd hoped. Yes, it's in Chrome and works, and the Firefox nightly builds, but IE10 requires a plugin that won't talk with Chrome or Firefox, and mobile support is truly lacking.
So I shifted my focus to a partial WebRTC polyfill. I wanted it so that if either the browser did not support WebRTC or could not tunnel to the "host" machine for the p2p session, then the systems would fallback to a lightweight server interface.
I managed to get something working in about four hours, and built a simple multiplayer game to demonstrate the technology. I'm not going to link to my demo as I hadn't put any security in place and I know of at least a dozen major security holes in my hack, but I'm happy. The characters were generated using the Charas character generator. I built a simple Perlin noise level generator to allow the world to be passed around using only four bytes, and I drew the tileset and even had support for animated terrain (water and lava flowed).
We had up to seven people running in the same game world, with the world being hosted on a Kindle Fire HD 8.9", and players on Windows Phone 8, IE10, Chromium on Linux, Firefox, and more. For some reason, it wouldn't work on the Fire HD 7" or Nexus 7 that we had nearby, but that happens when you only have twelve hours of development time. The judges weren't the target market for the polyfill, but the other developers who were there were really impressed and at least three were interested in using the polyfill once complete.
So what next? Well, the polyfill is going to require a lot more work to be released to anyone, and seems like a good evening project, and actually finishing a small game and demonstrating it reinvigorated me, so back to "SiN Episode 0."
I wasn't sure what to expect from AngelHack going into it. I'd found out about it on Thursday, and after much hemming, hawing, and gnashing of teeth, I decided to go for it.
I'm used to a GiveCamp-style model, where you've got your project well-defined in advance, but for this one I was able to decide for myself what I'd do within their restriction: your code must be completed within 24 hours.
I decided I'd use the opportunity to learn some "new-to-me" technology, specifically WebRTC's peer-to-peer data capabilities. However, I quickly found out that adoption isn't what I'd hoped. Yes, it's in Chrome and works, and the Firefox nightly builds, but IE10 requires a plugin that won't talk with Chrome or Firefox, and mobile support is truly lacking.
So I shifted my focus to a partial WebRTC polyfill. I wanted it so that if either the browser did not support WebRTC or could not tunnel to the "host" machine for the p2p session, then the systems would fallback to a lightweight server interface.
I managed to get something working in about four hours, and built a simple multiplayer game to demonstrate the technology. I'm not going to link to my demo as I hadn't put any security in place and I know of at least a dozen major security holes in my hack, but I'm happy. The characters were generated using the Charas character generator. I built a simple Perlin noise level generator to allow the world to be passed around using only four bytes, and I drew the tileset and even had support for animated terrain (water and lava flowed).
We had up to seven people running in the same game world, with the world being hosted on a Kindle Fire HD 8.9", and players on Windows Phone 8, IE10, Chromium on Linux, Firefox, and more. For some reason, it wouldn't work on the Fire HD 7" or Nexus 7 that we had nearby, but that happens when you only have twelve hours of development time. The judges weren't the target market for the polyfill, but the other developers who were there were really impressed and at least three were interested in using the polyfill once complete.
So what next? Well, the polyfill is going to require a lot more work to be released to anyone, and seems like a good evening project, and actually finishing a small game and demonstrating it reinvigorated me, so back to "SiN Episode 0."
April 18, 2013
HOWTO: Get a random sampling of lines from a text file in bash
Let's say you needed to get 150 lines at random from a text file and you were running Linux.
The solution involves a command I wasn't aware of until today...shuf.
The command:
shuf scrambles all the lines in the file; head pulls the set number of lines for you.
The solution involves a command I wasn't aware of until today...shuf.
The command:
shuf file-to-sample.txt | head -n 150
shuf scrambles all the lines in the file; head pulls the set number of lines for you.
April 8, 2013
Resolution Update
Yes, I know there have been no updates for the last few weeks. I've had some upheaval in my personal life that has prevented me from working on the #1 problem with my project...
It isn't fun to play.
It's functioning, but there's something missing.
Once I have time to work on it again (probably six to eight weeks), I'll start updating more, but in the meantime, I'm just trying to figure out where I twisted when I should have turned.
It isn't fun to play.
It's functioning, but there's something missing.
Once I have time to work on it again (probably six to eight weeks), I'll start updating more, but in the meantime, I'm just trying to figure out where I twisted when I should have turned.
March 13, 2013
Equipment Failure
Woke up this morning to the click of death on my game development drive.
Good news is I have everything backed up, and I've already ordered a replacement hard drive, so I should be back in business on Sunday.
Good news is I have everything backed up, and I've already ordered a replacement hard drive, so I should be back in business on Sunday.
March 9, 2013
SiN 1 in SiN: Episodes Reposted
My web host was hacked a couple of years ago, and when I moved web hosts, I didn't put back up every single file that I had before.
Today, I got an email asking about SiN 1 in SiN Episodes.
Fortunately, I still had a backup of that file, and it has been restored.
Today, I got an email asking about SiN 1 in SiN Episodes.
Fortunately, I still had a backup of that file, and it has been restored.
February 24, 2013
Resolution #7: Approaching First Playable
I'm getting close to a first playable on the first of the two titles I'm working on this year: USESP #0.
Those of you who were following me back when I worked for Ritual might recognize the acronym and/or be able to derive it.
I'm going low-resolution and low-fidelity for the graphics and models so I can try to focus on gameplay and filling in some backstory, specifically:
1) How did Elexis get her hands on Blade?
2) How did Jessica find Blade and get him out?
3) What happened between the time Elexis escaped Blade at the end of "SiN" and the beginning of Episode 1?
4) What DID Blade get injected with?
5) Why is Elexis dressing in a more business-like fashion?
If I am able to release this when I'm done, it will be under the "SiN Episodes" mod license, so you will have to own a copy of "SiN Episodes: Emergence" to play it.
Those of you who were following me back when I worked for Ritual might recognize the acronym and/or be able to derive it.
I'm going low-resolution and low-fidelity for the graphics and models so I can try to focus on gameplay and filling in some backstory, specifically:
1) How did Elexis get her hands on Blade?
2) How did Jessica find Blade and get him out?
3) What happened between the time Elexis escaped Blade at the end of "SiN" and the beginning of Episode 1?
4) What DID Blade get injected with?
5) Why is Elexis dressing in a more business-like fashion?
If I am able to release this when I'm done, it will be under the "SiN Episodes" mod license, so you will have to own a copy of "SiN Episodes: Emergence" to play it.
February 17, 2013
Resolution #6: Life (Gets In The) Way
No real progress to report today.
I have to stop by the office to take care of some things, unfortunately, and that will sap the six hours I usually have blocked out for personal projects on Sunday.
I have to stop by the office to take care of some things, unfortunately, and that will sap the six hours I usually have blocked out for personal projects on Sunday.
February 10, 2013
Resolution #5: Episodic Problem #1: Content
The first problem you are going to encounter when developing an episodic first person shooter is the variety of settings. Even if you have all the code done in advance, building the art assets for the constant variety demanded by today's consumer takes a significant amount of time, effort, resources, and money.
You end up with a choice: either keep your episodic shooter in a small, enclosed area so you can reuse most of your assets but everyplace has that "samey" feeling, or go big, but lose the economies of scale that most other forms of episodic gaming get.
Take the following (now hypothetical) location lists, for example:
SiN Episodes 0: Deception
HARDCorps Headquarters
- Office/Meeting Space (Cutscenes only)
- Training Center
- Medical Bay (Cutscenes only)
Abandoned City
- Hospital Interior
- Street
- Destroyed Warehouse
SiNTek Office
- Mutagen Lab (Decrepit)
- Blade injection room
- Lobby
Freeway
- Straightaway
- On/off ramps
SiN Episodes 1: Emergence
SiNTek Office
- Blade injection room
- Lobby
Docks
- Exterior
- Sewer
- Lighthouse
- Dockhouses
- Craneyard
- Warehouses
U4 Labs
- Dilapidated Sub
- Lab equipment area
Construction Site
Office Building
- Lobby
- Cube Farms
- Maintenance Area
- Building Exterior
SiN Episodes 2: Freefall
Forest (Limited locale)
Abandoned Aquarium/Zoo
- Tanks
- Caged Areas
- Maintenance/Vet Areas
Dam Under Repair
- Tractor beams
- Equipment railway
Caverns
- Lab (Decrepit)
HARDCorps Headquarters
- Medical Bay (Cutscenes only)
Not much in the way of potential asset reuse here, is there? Even if you are able to find pre-built assets for your game, assets are going to be a huge cost center for each new episode you choose to build.
Even for smaller games, the costs to create the worlds easily trumps the costs of creating the gameplay elements. Take a look at "Defense Grid: The Awakening." (No, seriously, take a look at it. The game rocks.) The basic elements of the game are rather straightforward: less than a dozen upgradable turrets and fifteen enemies that are reused on every single level, and a series of about fifty canned vocal blurbs that are used based on certain tower/enemy death combos. However, each level has a unique look and feel (20 different environments), plus there is custom spoken dialog on every level. The extra effort used to keep the same elements (enemies, weapons) from feeling "samey" costs a significant amount to create.
Telltale does a great job with their adventure games, but look more closely at how they construct their episodes the next time you play. You spend a long time in each individual location and will often revisit locations between episodes, making it easier to justify spending the effort on the location. They'll also do lots of modular work within an area so they can swap out, add or remove props to give an environment a new feel (Sam & Max's office, for example).
I'm not saying that this problem is insurmountable, but I am saying that this is a problem that will need to be solved before episodic first person shooters can truly become "episodic."
You end up with a choice: either keep your episodic shooter in a small, enclosed area so you can reuse most of your assets but everyplace has that "samey" feeling, or go big, but lose the economies of scale that most other forms of episodic gaming get.
Take the following (now hypothetical) location lists, for example:
SiN Episodes 0: Deception
HARDCorps Headquarters
- Office/Meeting Space (Cutscenes only)
- Training Center
- Medical Bay (Cutscenes only)
Abandoned City
- Hospital Interior
- Street
- Destroyed Warehouse
SiNTek Office
- Mutagen Lab (Decrepit)
- Blade injection room
- Lobby
Freeway
- Straightaway
- On/off ramps
SiN Episodes 1: Emergence
SiNTek Office
- Blade injection room
- Lobby
Docks
- Exterior
- Sewer
- Lighthouse
- Dockhouses
- Craneyard
- Warehouses
U4 Labs
- Dilapidated Sub
- Lab equipment area
Construction Site
Office Building
- Lobby
- Cube Farms
- Maintenance Area
- Building Exterior
SiN Episodes 2: Freefall
Forest (Limited locale)
Abandoned Aquarium/Zoo
- Tanks
- Caged Areas
- Maintenance/Vet Areas
Dam Under Repair
- Tractor beams
- Equipment railway
Caverns
- Lab (Decrepit)
HARDCorps Headquarters
- Medical Bay (Cutscenes only)
Not much in the way of potential asset reuse here, is there? Even if you are able to find pre-built assets for your game, assets are going to be a huge cost center for each new episode you choose to build.
Even for smaller games, the costs to create the worlds easily trumps the costs of creating the gameplay elements. Take a look at "Defense Grid: The Awakening." (No, seriously, take a look at it. The game rocks.) The basic elements of the game are rather straightforward: less than a dozen upgradable turrets and fifteen enemies that are reused on every single level, and a series of about fifty canned vocal blurbs that are used based on certain tower/enemy death combos. However, each level has a unique look and feel (20 different environments), plus there is custom spoken dialog on every level. The extra effort used to keep the same elements (enemies, weapons) from feeling "samey" costs a significant amount to create.
Telltale does a great job with their adventure games, but look more closely at how they construct their episodes the next time you play. You spend a long time in each individual location and will often revisit locations between episodes, making it easier to justify spending the effort on the location. They'll also do lots of modular work within an area so they can swap out, add or remove props to give an environment a new feel (Sam & Max's office, for example).
I'm not saying that this problem is insurmountable, but I am saying that this is a problem that will need to be solved before episodic first person shooters can truly become "episodic."
February 3, 2013
Resolution #3, #4: Dead End, Pain
Nothing new to report.
Weekend #3 was just a realization that I was going down a dead end and replanning some items.
This weekend, I've been recovering from kidney stones.
Hopefully more to report next weekend.
Weekend #3 was just a realization that I was going down a dead end and replanning some items.
This weekend, I've been recovering from kidney stones.
Hopefully more to report next weekend.
January 14, 2013
Resolution #2: Minimal Update
Sunday was spent mostly with architecture. Most of my game world is going to be grid based, so I'm setting up visibility calculations accordingly.
I hope to have something rendering by end of month.
I hope to have something rendering by end of month.
January 6, 2013
Resolution #1: Discussion
There are generally two types of New Year's resolutions: those where someone tries to improve themselves by correcting a perceived failing of their own, and those where someone tries to reach a goal that has been eluding them for some period of time. Both require publicly talking about them because once you talk about a resolution with someone, you are more committed to the path.
I have one resolution this year, and it is one of the latter ones. Specifically, I want to design, create, and prepare for release one new game this year. I may not be able to release it (yet) due to a clause in my employment agreement, but I can create it, and talk about my progress as I work through it.
The reason behind this resolution is two-fold. First, I've been out of game development for six years now, and I've not been able to successfully complete any of my side game projects during this window, either due to time constraints from work or motivation issues. Second, I've had a fairly significant question since we released "SiN Episodes: Emergence" and since Valve released "Half-Life 2: Episode 2" that I've been struggling to answer.
The real question is: why is it so hard to successfully release an episodic first person shooter? I think I have an answer for it, and it's that answer I want to explore this year. Specifically, I think the issue is the toolset we have for developing shooters.
Levels in first person shooters are generally broken out into encounter areas and connecting areas. An encounter area is a highly polished, highly produced area, and a connecting area is usually a hallway or some simpler area designed solely to get you from encounter area to encounter area. Combat may occur in both areas, but the encounter areas are what people remember. Connecting areas are designed to be forgettable.
The issue is that we have the same toolset for both, this toolset is designed to make construction of encounter areas easier, and generally this toolset makes iteration expensive to do. The really sad part is that due to the standardized rules of video games, we have the proper tools for both, but this toolset isn't properly linked.
Look at the simple level designer built into "Portal 2" and look at "Hammer" in the Source SDK. Using the "Portal 2" editor lets people build amazingly complex levels, including ones with almost the exact same puzzles as shipped in the retail game in a fraction of the time, but they all look the same...there is nothing memorable. However, you can quickly iterate on the puzzles and get them to the point where the gameplay is there.
Trying to build the same levels in "Hammer" takes significantly longer, but you have significantly more flexibility, can make each level visually and thematically distinct, and as a result make them more memorable. However, iteration takes significantly longer.
I'm thinking of writing a toolkit to try to bridge these two worlds: a level editor that works mostly like the "Portal 2" editor so that you can quickly generate your environments and iterate on the gameplay, and then once you are ready to take an area and turn it into a proper encounter area, you can select that area, export it out as an encounter area in an editable format for something like "3D Studio MAX" or "Hammer" or "3D World Studio," and then edit it using the proper tool for the proper job. Once that is done, that encounter area acts just like a building block. You can move it around like a block in the initial editor and have all of the links properly wired up as the connecting areas get additional edits, and the encounter area can also be handed off to other people for concurrent work.
I only have Sunday's blocked out for this work, but I think I can get this done. I'm going to be using Visual Studio 2012 Professional as my development suite, with Visual C# used for the tools and Visual C++ used for the game code.
Today is all design and project setup. Next Sunday is code time.
I have one resolution this year, and it is one of the latter ones. Specifically, I want to design, create, and prepare for release one new game this year. I may not be able to release it (yet) due to a clause in my employment agreement, but I can create it, and talk about my progress as I work through it.
The reason behind this resolution is two-fold. First, I've been out of game development for six years now, and I've not been able to successfully complete any of my side game projects during this window, either due to time constraints from work or motivation issues. Second, I've had a fairly significant question since we released "SiN Episodes: Emergence" and since Valve released "Half-Life 2: Episode 2" that I've been struggling to answer.
The real question is: why is it so hard to successfully release an episodic first person shooter? I think I have an answer for it, and it's that answer I want to explore this year. Specifically, I think the issue is the toolset we have for developing shooters.
Levels in first person shooters are generally broken out into encounter areas and connecting areas. An encounter area is a highly polished, highly produced area, and a connecting area is usually a hallway or some simpler area designed solely to get you from encounter area to encounter area. Combat may occur in both areas, but the encounter areas are what people remember. Connecting areas are designed to be forgettable.
The issue is that we have the same toolset for both, this toolset is designed to make construction of encounter areas easier, and generally this toolset makes iteration expensive to do. The really sad part is that due to the standardized rules of video games, we have the proper tools for both, but this toolset isn't properly linked.
Look at the simple level designer built into "Portal 2" and look at "Hammer" in the Source SDK. Using the "Portal 2" editor lets people build amazingly complex levels, including ones with almost the exact same puzzles as shipped in the retail game in a fraction of the time, but they all look the same...there is nothing memorable. However, you can quickly iterate on the puzzles and get them to the point where the gameplay is there.
Trying to build the same levels in "Hammer" takes significantly longer, but you have significantly more flexibility, can make each level visually and thematically distinct, and as a result make them more memorable. However, iteration takes significantly longer.
I'm thinking of writing a toolkit to try to bridge these two worlds: a level editor that works mostly like the "Portal 2" editor so that you can quickly generate your environments and iterate on the gameplay, and then once you are ready to take an area and turn it into a proper encounter area, you can select that area, export it out as an encounter area in an editable format for something like "3D Studio MAX" or "Hammer" or "3D World Studio," and then edit it using the proper tool for the proper job. Once that is done, that encounter area acts just like a building block. You can move it around like a block in the initial editor and have all of the links properly wired up as the connecting areas get additional edits, and the encounter area can also be handed off to other people for concurrent work.
I only have Sunday's blocked out for this work, but I think I can get this done. I'm going to be using Visual Studio 2012 Professional as my development suite, with Visual C# used for the tools and Visual C++ used for the game code.
Today is all design and project setup. Next Sunday is code time.
Subscribe to:
Posts (Atom)