A Unity3D script to prevent your precious web game from being pinched from authorised websites and just hosted somewhere else.
This sort of very simple protection is especially important if you are trying to monetize your web game and do not want it spread around on websites where you don’t see any financial compensation.
If you host on Wooglie.com or Shockwave.com, I highly recommend you at least drop this script in to your project. It’s one more line of defence against random people just swiping your game and hosting it on their own advertising supported website.
How to use: Copy and paste this code in to a new C# script named AntiPiracy.cs and then attach the script to one of your game objects, such as the player, or the main menu. You only need to use it once, and it only needs to execute once.
You specify the list of permitted remote hosts in the Unity3D Inspector. There can be multiple remote hosts, though you should generally create a new build for each website you release your game on as there can only be a single bounce URL. For Wooglie.com your remote hosts would be http://wooglie.com, http://www.wooglie.com and http://contentmirror.wooglie.com. You must put http:// or https:// in front of each of your remote hosts for the script to work. The script is reasonably smart but it cannot work if you give it bad input.
You can change the list of permitted local hosts too, though you shouldn’t need to unless you are doing something unusual. Local hosts are used when you run your game in a web browser from file:// or http://localhost/.
You can specify whether to permit running on local host with a simple check box. This lets you test your game in a web browser by using Build and Run (CTRL+B on Windows) without having to do anything special.
If the piracy test fails, you specify a URL for the web browser to redirect the player to. This can be anything you want, but should probably be the homepage of the game or the correct page for the game on Wooglie.com or Shockwave.com.
You can manually invoke the piracy test by calling the function TestPiracy(). You could test at the beginning of every level if you so desire. Though one test is usually sufficient.
The script runs automatically at start-up. You can disable this behaviour by commenting out the function Start() and then manually invoking the piracy test.
If you want to be really sneaky, you can wait a few levels, or a few minutes before bouncing the player out of the game.
When you create a release build I suggest you disable Permit Local Host in the Inspector unless you want your players to be able to download and play your game from their desktop.
How it works: The script maintains a list of websites that are permitted to host your game. When the script starts up it looks at website it is being hosted on, and if it doesn’t match up with one of the permitted websites you’ve specified, it bounces the player to a webpage that you’ve specified.
Two tests are performed, one with the Unity3D web player, and another in JavaScript. The quick JavaScript test makes sure the Unity3D web player isn’t being spoofed somehow.
Assumptions: This isn’t going to stop anybody really determined. It’s a simple matter for anyone willing to take the time to decompile your entire game and edit the strings containing the list of permitted websites. What it will do is prevent 90% or more of the websites that just harvest random
The License: It’s the GNU Lesser GPL. That means you can take it, modify it, put it in your own game, and so on. You have to leave the copyright notice in place, if you create a modified version and distribute it, you have to supply the source code. You cannot sell the AntiPiracy script, but you can include it in a product that is for sale.
If you represent Wooglie.com, Shockwave.com or any of the other websites out there that host Unity3D games, feel free to link directly to this webpage, and also host the file directly on your own webpage so long as the entire file, including copyright information, remains intact.
/*-----------------------------------------------------------------------------
* AntiPiracy.cs - Permits the game only to run on allowed hosts
* Copyright (C) 2010 Justin Lloyd
* http://www.otakunozoku.com/
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
-----------------------------------------------------------------------------*/
using UnityEngine;
using System.Collections;
using System;
using System.Collections.Generic;
using System.Text;
public class AntiPiracy : MonoBehaviour
{
/// <summary>
/// Do we permit execution from local host or local file system?
/// </summary>
public bool m_permitLocalHost = true;
/// <summary>
/// List of permitted remote hosts that host this game.
/// </summary>
public string[] m_permittedRemoteHosts;
/// <summary>
/// List of permitted localhost URLs
/// </summary>
public string[] m_permittedLocalHosts = { "file://", "http://localhost/",
"http://localhost:", "https://localhost/", "https://localhost:" };
/// <summary>
/// URL to bounce the player to if they are executing the game from an
unknown URL.
/// </summary>
public string m_bounceToURL;
void Start()
{
TestPiracy();
}
/// <summary>
/// Determine if the current host exists in the given list of permitted
hosts.
/// </summary>
/// <param name="hosts">An array of hosts permitted to host this game.
</param>
/// <returns>True if the current host is permitted to host this game.
</returns>
private bool IsValidHost(string[] hosts)
{
// print out list of hosts in debugging build
if (Debug.isDebugBuild)
{
StringBuilder msg = new StringBuilder();
msg.Append("Checking against list of hosts: ");
foreach (string url in hosts)
{
msg.Append(url);
msg.Append(",");
}
Debug.Log(msg.ToString());
}
// check current host against each of the given hosts
foreach (string host in hosts)
{
if (Application.absoluteURL.IndexOf(host) == 0)
{
return true;
}
}
return false;
}
/// <summary>
/// Determine if the current host is a valid local host.
/// </summary>
/// <returns>True if the game is permitted to execute from local host and
/// the current host is local host.</returns>
public bool IsValidLocalHost()
{
if (m_permitLocalHost)
{
return IsValidHost(m_permittedLocalHosts);
}
return false;
}
/// <summary>
/// Determine if the current host is a valid remote host.
/// </summary>
/// <returns>True if the game is permitted to execute from the remote host.
</returns>
public bool IsValidRemoteHost()
{
return IsValidHost(m_permittedRemoteHosts);
}
/// <summary>
/// Bounce the player to game's home page
/// </summary>
public void Bounce()
{
Application.OpenURL(m_bounceToURL);
}
/// <summary>
/// Determine if the current host is a valid host (local or remote)
/// </summary>
/// <returns>True if the current host is permitted to host the game.
</returns>
public bool IsValidHost()
{
if (IsValidLocalHost() == true)
{
return true;
}
if (IsValidRemoteHost() == true)
{
return true;
}
return false;
}
/// <summary>
/// Compile a list of hosts in to a fragment of JavaScript.
/// </summary>
/// <param name="permittedHosts">List of hosts permitted to host the
game.</param>
/// <returns>Fragment of JavaScript for testing the current host.
</returns>
private string CompileHosts(string[] permittedHosts)
{
StringBuilder hosts = new StringBuilder();
for (int i = 0; i < permittedHosts.Length; i++)
{
hosts.Append("(document.location.host != '");
string url = permittedHosts[i];
if (url.IndexOf("http://") == 0)
{
url = url.Substring(7);
}
else if (url.IndexOf("https://") == 0)
{
url = url.Substring(8);
}
hosts.Append(url);
hosts.Append("')");
if (i < permittedHosts.Length - 1)
{
hosts.Append(" && ");
}
}
return hosts.ToString();
}
/// <summary>
/// Perform a browser check using JavaScript to determine if the current
/// host is permitted to host the game.
/// </summary>
private void CheckWithJavaScript()
{
StringBuilder javascriptTest = new StringBuilder();
javascriptTest.Append("if (");
// compile test for local hosts
if (m_permitLocalHost)
{
javascriptTest.Append("(document.location.host != 'localhost') &&
(document.location.host != '')");
if (m_permittedRemoteHosts.Length > 0)
{
javascriptTest.Append(" && ");
}
}
// compile test for remote hosts
javascriptTest.Append(CompileHosts(m_permittedRemoteHosts));
javascriptTest.Append("){ document.location='");
javascriptTest.Append(m_bounceToURL);
javascriptTest.Append("'; }");
if (Debug.isDebugBuild)
{
Debug.Log(javascriptTest);
}
Application.ExternalEval(javascriptTest.ToString());
}
/// <summary>
/// Perform a complete check to see if the current host is permitted to
/// host the game. Bounce the player to the game's home page if it is not.
/// </summary>
public void TestPiracy()
{
if (Debug.isDebugBuild)
{
Debug.Log(String.Format("The absolute URL of the application is
{0}", Application.absoluteURL));
}
if (Application.platform != RuntimePlatform.WindowsWebPlayer &&
Application.platform != RuntimePlatform.OSXWebPlayer)
{
Debug.Log("Testing for piracy but not in web browser, so not
worrying about it.");
return;
}
// if it's not a valid remote host, bounce the user to the proper URL
if (IsValidHost() == false)
{
if (Debug.isDebugBuild)
{
Debug.Log(String.Format("Failed valid remote host test.
Bouncing player to {0}", m_bounceToURL));
}
Bounce();
return;
}
// it might appear to be a valid local or remote host, but one final
check in JavaScript to verify that
CheckWithJavaScript();
}
}
Posted in Games Industry, Software Development | No Comments »
Need a generic scoring and level behaviour for Unity3D? This should work for you. I use a script similar to this in quite a few of my games.
How to use: Copy and paste this code in to a new C# script named Scoring.cs and then attach the script to your player object, GUI object, or whatever needs to keep track of a score.
How it works: The player’s level and score are tracked by way of two simple integer variables. Score starts at 0, and level starts 1. You can change these starting values by assigning new ones in some other script which is useful when you restore a previously saved game.
A list of scores is stored and compared against each Update() to see if the player has enough points to level up. The level up can be as simple as the points scored in Space Invaders or more complex such as tracking experience in an RPG.
Usually in a video game, the first few levels have a non-linear progression of points required to level up. You can specify this non-linear progression by entering the points required for each of the first few levels in the Inspector. Take a look at the member variable m_nextLevelScore, which is exposed in the Unity3D inspector as an array of integers. Once you have specified the first few point values (default values are given in the source code), you specify how many more points are required per level beyond the non-linear progression.
Take a look at the code, at the top of the class you’ll see that the member variable m_nextLevelScore has some default values. To go from level 1 to level 2 requires 3,000 points. From level 2 to level 3, 7,000 points are required. And so on. When you hit the last value in the list, 80,000 points, the progression becomes linear, so the member variable m_nextLevelScoreProgression takes over, requiring 100,000 points for the level after level 10, i.e. the 80,000 points. Every level from then on will require an additional 100,000 points.
The maximum level that can be reached is handled by the member variable m_maximumLevel. The default is set at 100, but you can set it to any value you like. The function that checks for level up, CheckForLevelUp() is virtual, so you can override it in an inherited class to instead handle a computed level progression similar to Fallout 3.
You can specify a sound effect that will play when the player levels up using the m_nextLevelSound member variable which is exposed in the Inspector. You can specify multiple sounds, or just one, or even none at all. If you only have a single level up sound effect, the same one will be used repeatedly. If you have more than one, but not a distinct sound effect for every individual level then the last sound effect specified in the list will be used for each level beyond the number of sound effects available.
Assumptions: Negative levels are not supported. Levelling down is not supported. Negative scores work, but probably shouldn’t be used.
Note: Oops! Posted the wrong version of the code earlier that was for a specific piece of game logic. This one fixes the oversight. If you downloaded the other code already, switch it out for the updated version posted below. It gets rid of the limitation on ridiculously high maximum levels.
The code:
/*-----------------------------------------------------------------------------
* Scoring.cs - Keeps track of the player's score and levels up as required.
* Copyright (C) 2010 Justin Lloyd
* http://www.otakunozoku.com/
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*
-----------------------------------------------------------------------------*/
using UnityEngine;
using System;
using System.Text;
[RequireComponent(typeof(AudioSource))]
public class Scoring : MonoBehaviour
{
/// <summary>
/// Audio clips to play for each level up sound.
/// </summary>
public AudioClip[] m_nextLevelSound;
/// <summary>
/// Maximum permitted level.
/// </summary>
public int m_maximumLevel = 100;
/// <summary>
/// The list of scores required to advance to the next level.
/// </summary>
public int[] m_nextLevelScore = { 0, 3000, 7000, 12000, 18000, 25000, 34000,
44000, 56000, 69000, 80000 };
/// <summary>
/// The number of required points to score to advance to the next level once
the score has gone beyond the provided list of points.
/// </summary>
public int m_nextLevelScoreProgression = 100000;
/// <summary>
/// The player's current score.
/// </summary>
private int m_score = 0;
/// <summary>
/// The player's current level.
/// </summary>
private int m_level = 1;
/// <summary>
/// The minimum level permitted.
/// </summary>
private const int MinimumLevel = 1;
/// <summary>
/// The player's score.
/// </summary>
public int Score
{
get
{
return m_score;
}
set
{
m_score = value;
}
}
/// <summary>
/// Adjust the score by the specified number of points. Negative values
/// will subtract points.
/// </summary>
/// <param name="points" />Number of points to adjust the current score
by.</param>
public void AdjustScore(int points)
{
m_score += points;
}
/// <summary>
/// Adjust the current level by the specified number of levels. Negative
/// values will subtract levels. Does not adjust the score to match. The
/// new level will be clamped to within the maximum permitted level.
/// </summary>
/// <param name="levels" />Number of levels to adjust the current level
by.</param>
public void AdjustLevel(int levels)
{
m_level = Mathf.Clamp(m_level + levels, MinimumLevel, m_maximumLevel);
}
/// <summary>
/// The player's current level. Specifying a new level will ensure that the
/// new level is clamped to the maximum permitted level.
/// </summary>
public int Level
{
get
{
return m_level;
}
set
{
m_level = Mathf.Clamp(value, MinimumLevel, m_maximumLevel);
}
}
/// <summary>
/// Play the audio for level up sound.
/// </summary>
public void PlayNextLevelSound()
{
int levelUpIndex = Mathf.Clamp(m_level, MinimumLevel,
m_nextLevelSound.Length - 1) - 1;
if (m_nextLevelSound[levelUpIndex] == null)
{
return;
}
this.audio.PlayOneShot(m_nextLevelSound[levelUpIndex]);
}
/// <summary>
/// Checks for completion of the current level and advances to the next
/// level if the score is high enough.
/// </summary>
public virtual void CheckForLevelUp()
{
// if we have reached the maximum level, do nothing
if (m_level >= m_maximumLevel)
{
return;
}
// check for the next required score
int nextLevelScore = 0;
// if there are no more scores in the level score progression array
// switch over to linear progression
// otherwise, use the non-linear progression
if (m_level >= m_nextLevelScore.Length)
{
nextLevelScore = (m_level - m_nextLevelScore.Length + 1) *
m_nextLevelScoreProgression;
}
else
{
nextLevelScore = m_nextLevelScore[m_level];
}
// if we have the required score to level up, advance to the next level
if (m_score >= nextLevelScore)
{
m_level = Math.Min(m_level + 1, m_maximumLevel);
PlayNextLevelSound();
}
}
void Update()
{
CheckForLevelUp();
}
}
Posted in Games Industry, Software Development | No Comments »
Related posts:
This post isn’t about carpentry, it’s about Apple.
A bad carpenter blames his tools.
A good carpenter blames his tools, but for different reasons.
A bad carpenter will blame his tools uncritically when he can’t achieve the end effect that he desires, because he lacks the skill, the know-how and the wherewithal to produce the desired outcome. His frustration with the tool is borne of inexperience.
A good carpenter blames his tools with a sensibility and criticism not available to an unskilled initiate because he sees the inefficiencies in them, because he can produce the outcome he desires but may have to jump through hoops to get there, he blames the saw for ruining a perfectly good cut because it snagged in the last two inches and he most likely knows why it snagged and how to stop it happening again. His blame is a criticism of the essential qualities of the tool, not the tool itself.
I have frequent conversations with people about carpentry and they often want to know which brand of carpentry tools I prefer, or what particular tools I used to create something. These are people who are not carpenters themselves. They worry about the non-essential details, “Was that a Craftsman hammer you used or a Wilton brand?”
Tear down all barriers to carpentry tools. Give a large collection of extremely high quality carpentry tools to a number of people, running the gamut from utterly inexperienced to highly experienced. There’s no barrier to entry, only the desire to create something.
A whole lot of wood shavings and one or two masterpiece pieces of furniture. Maybe even a few craftsman houses. But only from those experienced enough to create them.
Now put up an artificial barrier, the tools are hard to use, their expensive, and only available to people who are willing to put in years of time and dedication in learning them before they are permitted to make anything.
What do you get?
A very small amount of people willing to commit to creating anything.
Ignoring the economics of production, labour demand and all of the other things, what you have now is a scarcity economy where only the most dedicated will create anything, willing to jump through artificial hoops to produce anything. Only the most well heeled can afford to pay the prices for carpentry.
When I am at parties and gatherings not frequented by game developers, the people I meet and converse with often ask what language I write my games in, as though the choice of C++, or assembler, or Python or even Visual BASIC, i.e. the tools I use, has any bearing on the quality of the work I am capable of producing. I’ve put in the time, I’ve learned my craft, give me the means to production, and whatever the tools at hand, I will produce something.
These people asking the question, whilst interested in what I do, don’t really understand the question they are asking. They are so out of touch with software development and video game production I might as well lie to them because they wouldn’t know any different.
I don’t worry about the brand of pots and pans that a chef friend of mine uses in his restaurant kitchen because whilst I dabble in the culinary arts, what he does and at the level he does it, I cannot even grasp. His years of training and work experience have taught him, in a way akin to osmosis, about how chemistry works and combines in the human mouth and nose to produce an entire experience. The same goes for me when I write code. I know how all of the parts fit together in the big picture, the individual statements in a function or the functions in a module are immaterial to the process of writing software, most of the time.
When Steve Jobs states that applications or games written in anything but Objective-C or C++ produces crap, it only shows just how out of touch he actually is with the whole world of software development.
It makes me cringe to hear it, that someone purportedly so tech savvy could be so ignorant. Put him in the same category of people at the party wondering if I use C++ to write my games. The answer is “No, I use whatever gets the job done. The tool is irrelevant.”
If the questioner, and Steve Jobs, knew anything about game development and modern software development, he’d know that very little of the actual game is written in C++ or Obective-C these days, just as very little of an operating system is written in assembly language anymore. The engine of the software, yes, C++ or some other arcane language. The rest is script, such as Lua, or a variation JavaScript or some other higher level scripting language, coupled with XML and other ancillary data created by people who wouldn’t know how to write a C++ functor if their lives depended on it.
The problem doesn’t stem from the tool. The problem begins with the easy access by inexperienced carpenters banging ugly holes in the wall with a sledgehammer or making off-square cuts because they haven’t yet learnt their trade. They’re jumping in feet first, creating bad applications and games in whatever tools at hand, not because the tools that they use are bad, but because the barriers to entry of acquiring those tools are low and the barrier to putting it out there in front of the public even lower.
When the tools are easy to use, people will create, even if they create utter rubbish. When the tools are hard, sometimes artificially so and sometimes because the maker of the tool was not particularly clever and sometimes because the maker of the tool was too damn clever, the craftsman will often spend years honing his skills before he produces anything worthwhile and worthy of charging for or that people are willing to pay for.
We’re not seeing bad work from bad tools, we’re seeing bad work from bad workmanship. Putting in an artificial barrier such as only creating in C++ or Obective-C isn’t going to stop this because artificial barriers never succeed.
Disclaimer: I do not condone stealing software (creators have got to eat), I figured that in this case, it was fair use and I was grateful to a friend for pointing me in the right direction.
Last week I was able to download a torrent of thousands of iPhone and iPad games. Many more than I needed for my sample. After removing duplicates, i.e. the iPad release and the iPhone release of the same game, and also removing all games prior to August 2008. After a few hours of tinkering with Perl I had a script that could scan each of the apps and look for sequences of bytes that would identify what software was used to produce the game.
|
Technology Used |
Number of Games |
Percentage of Games |
|
Pure C++ or Objective-C |
789 |
26.3% |
|
Unity |
255 |
8.5% |
|
Flash, Titanium, etc |
570 |
19.0% |
|
C++/Objective-C with Lua/Python/Javascript/other scripting langauge |
726 |
24.2% |
|
Non-C++/Objective-C framework, i.e. I don’t know what it is, but it’s not C++ |
660 |
22.0% |
| Total Games Sampled |
3000 |
Many of the games that are not pure C++ or Objective-C have been huge hits, selling many tens of thousands of copies. These aren’t exceptions to the rule, they are the norm. Games that I consider to be total dross that couldn’t have taken more than a few weeks are coded up in Objective-C or C++ and nothing else. Games that are top ten hits created in Flash or in Lua on top of a usually quite small C++ game engine.
Need For Speed Shift? Pure C++? Nope.
Mirror’s Edge? Pure C++? Nope.
Baseball Superstars 2010? Pure C++? Not even close.
Civ Revolutions? Pure C++? Don’t make me laugh.
Monkey Island 2? Pure C++? In your dreams.
I have sat on the fence about writing this post for a long time, ever since the 3.3.1 debacle.
Frankly, this entire Apple broohaha is just sabre rattling on their part. If Apple really wanted to fix it, they would create a more stringent submission process that actually tested for things like quality and playability, much like SONY or Microsoft or Nintendo do on their platforms.
But of course, they don’t, and they won’t, because having a quarter of a million applications in your online store gives you a certain buzz when comparing the size of your wang against everybody else’s.
Posted in Articles, Games Industry, Software Development | No Comments »
Related posts: