Thursday, November 2, 2017

Resolving Mixer interactivity issues.

Recently I have stumbled in my attempt to get the Mixer interactive feature of my game working.

As you can see, I've created two scenes in my Mixer project:


And this:


I then clicked on the publish link and was presented with this choice:


October 10, 2017: Solving the different serialization layout errors.

The first time I built an UWP rendition of my game I was plagued with different serialization layout errors:

A script behaviour (probably [My component]?) has a different serialization layout when loading.

This error seemed to appear for each of my custom components.

At first I did exactly as the error messages suggested: I removed all #ifdef statements my code.  While doing so, I realized none of them encapsulated any public fields of any of my game components (classes descended from Unity3D's MonoBehavior class), so obviously precompiler directives were not the issue.

I then did some googling and found a site that claimed Unity3D sometimes serialized all fields, so I added the [NotSerialized] attribute to my protected and private fields.  I even changed my getters and setters into methods; again no luck.

Then I decided to focus on the Unity3D project.  I viewed my plugins in the Inspector and noticed that the “Do not process checkbox” was checked for each.  Based on the article here (https://docs.unity3d.com/Manual/windowsstore-plugins.html), which clearly states that Unity injects serialization code into plugins, I unchecked the check boxes for each of my plugins.

And voila!  That resolved my issue!

Sept 29, 2017: Why I decided to build my game as Unity3D plugins.

When I first started game making in Unity3D, I used the default method of creating scripts in the assets folder.  I found this to be very easy as Unity took care of all the prep work for me, so I was left free to focus on coding the game logic itself.

However this limited my ability to properly organize my source code in my repository.  To summarize, I discovered the following limitations:

1.  It was difficult to divide out shared code into a separate Visual Studio project for sharing across different applications.

2.  It was difficult to include namespaces not already bundled with Unity3D.

To resolve this, I decided to put my code into plugins for Unity3D.  This provided the flexibility to properly organize my source tree and easily add UWP libraries, at the expense of tying myself to the Microsoft platform.  However I had already decided early on to only code on the Microsoft.NET platform anyway.

So I organized my work into two UWP projects, one as a library of components I would share between Unity3D based games, and the other as a set of components specific to my current game.

I then created a .NET standard library targeting .NET standard 1.4.  My intention was to use this as a library of all my message contracts.  Obviously .NET standard made it easy to put my message contracts on both the Unity3D client and the server, which I intended to be Azure cloud.

I dragged my libraries into a folder structure I created underneath the Assets folder in my Unity3D project, and at first I was still able to see the components I created still attached to the GameObjects within the scene.  However, when I tried to build an UWP solution for my game using the Unity3D build wizard, it failed because one of my UWP projects was targeting a namespace specific to UWP and .NET 4.7, and Unity3D was trying to compile against .NET 3.5.

So then I tried to set my UWP plugins to only run on UWP by clicking on the DLLs in Unity3D’s asset view window and setting their properties using the inspector.  However, this only made me unable to run my game in Unity editor.  I also noticed that while in editor view, my components had disappeared from the inspector view of my GameObjects, with the editor complaining they could no longer be found.

After using my best friend Google, I discovered this link:

I decided to give it a shot.

First, I created another solution in my source tree to hold plugin placeholder projects.  I added two Visual Studio projects into this solution, both targeting the regular .NET 3.5 framework (the same one Unity’s Mono targets), one for each of my UWP projects.

Second, within these I added appropriate references to UnityEngine.dll, located here:
C:\Program Files\Unity2017.1\Editor\Data\Managed
And UnityEngine.UI.dll, located here:
C:\Program Files\Unity2017.1\Editor\Data\UnityExtensions\Unity\GUISystem

Third, I used NuGet to add in appropriate libraries.

Fourth, I added links from these placeholder projects back to the source code in the UWP projects.

Fifth, I created a class with the same name as within my UWP project that referenced an UWP only library, and made it a dummy class that Unity could serialize against and didn’t reference any UWP libraries within.

I put my placeholder classes library in the original folder located underneath the parent Assets folder.

I moved my UWP assemblies into an UWP subfolder beneath.

I went to the inspector and set the UWP assemblies to only run on UWP, made sure they would not process, and set that as placeholders to their equivalent libraries that I just made, targeting regular .NET 3.5 framework.

Now when I open my game in Unity editor, I’m able to see my custom components, add these components to GameObjects, and see their properties as well as set them.

I was then able to get my game to build into an UWP solution, and used Visual Studio to deploy them to both my laptop and my Xbox One console.

However, when I took a screen capture of my test game on my Xbox One console, I get this:


Friday, April 3, 2015

Presenting at SATURN 2015.



I will be running an interactive session on architecting websites to handle high concurrent user load.  Will recommend two patterns that you should always keep in mind when building public facing websites.

I based some of this on the research I did for my MSDN Magazine article:
http://derricksweng.blogspot.ca/2013/02/building-simple-comet-application-using.html

Sunday, February 3, 2013

Building a simple COMET application using Microsoft.NET

MSDN Magazine published my article on using COMET principles and Microsoft.NET to implement web real-time. http://msdn.microsoft.com/en-ca/magazine/jj891053.aspx

Saturday, April 30, 2011

ASP.NET Drop down list and strongly typed DataTable

I had code where I populate the contents of an ASP.NET DropDownList with a strongly typed DataTable returned from an ASP.NET web service. In this code I bound the DataTable to the DataSource of the DropDownList and did a DataBind() in the Page_Load() method. This worked perfectly fine on my local box.

However, when I loaded up the ASP.NET page in the production data center, I got:

System.Web.HttpException: A
DropDownList cannot have multiple items selected

To solve this problem, after I bound the DataTable to the DropDownList, I iterated through each ListItem and set its Selected property to false:

foreach (ListItem myItem in myDropDownList)
{
myItem.Selected = false;
}

Wednesday, March 3, 2010

Calling an ASP.NET web service from a Java application

First, the Java application must contain a class that will encapsulate the functionality to invoke a XML web service via SOAP calls. The code is below:

import java.net.*;
import java.io.*;
import org.w3c.dom.Document;
import org.w3c.dom.*;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

public class WebServiceInvoker {
private String HTTPContinue = "^.*HTTP/[0-9].[0-9] 1[0-9][0-9].*$";
private String HTTPOk = "^.*HTTP/[0-9].[0-9] 200.*$";
private String HTTPSuccess = "^.*HTTP/[0-9].[0-9] 2[0-9][0-9].*$";
private String HTTPRedirect = "^.*HTTP/[0-9].[0-9] 3[0-9][0-9].*$";
private String HTTPClientError = "^.*HTTP/[0-9].[0-9] 4[0-9][0-9].*$";
private String HTTPServerError = "^.*HTTP/[0-9].[0-9] 5[0-9][0-9].*$";

private String _namespace;
private String _host;
private String _path;
private int _port;

private int _timeout;

/**
* @param host The server hosting the web service
* @param port The port on the server which will accept the call (default 80)
* @param wsPath The relative path of the web service on the server
* @param namespace The namespace defined in the webservice ("http://tempuri.org")
*/
public WebServiceInvoker(String host, int port, String wsPath,
String namespace) {
_host = host;
_port = port;
_path = wsPath;
_namespace = namespace;
_timeout = 60000;
}

public void setTimeout(int timeout) {
_timeout = timeout;
}

public int getTimeout() {
return _timeout;
}

public String invokeRPC(String remoteProcedure, ParameterCollection params)
throws Exception {
String result = "";

try {
String xmlData = ""
+ ""
+ "" + "<" + remoteProcedure + " xmlns=\""
+ _namespace + "\">";
for (int i = 0; i < params.size(); ++i) {
String[] sParam = params.Get(i);
if (sParam.length == 2) {
String paramName = sParam[0];
String paramValue = GeneralUtil.FixSpecialChar_forXML(sParam[1]);

xmlData += "<" + paramName + ">";
xmlData += paramValue;
xmlData += "";
} else {
System.out.println(xmlData);
throw new Exception("Poorly formatted parameter returned from Parameter Collection. WsTester.testWs");
}
}

xmlData += "" + "
"
+ "
";
InetAddress addr = InetAddress.getByName(_host);
Socket sock = new Socket(addr, _port);
sock.setSoTimeout(_timeout);

// Send header
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
wr.write("POST " + _path + " HTTP/1.1\r\n");
wr.write("Host: " + _host + "\r\n");
//wr.write("Content-Type: application/soap+xml; charset=\"utf-8\"; action=\"http://[URL to ASP.NET web service]\" \r\n");
wr.write("Content-Type: application/soap+xml; charset=\"utf-8\"; action=\"\" \r\n");
wr.write("Content-Length: " + xmlData.length() + "\r\n");
wr.write("\r\n");

// Send data
System.out.println("Soap Message:\n" + xmlData);
wr.write(xmlData);
wr.flush();

// Response
BufferedReader rd = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String line = "";
String header = "";
boolean bContinue = true;
int byteCount = 0;
// Read Header
while (bContinue) {
do {
line = rd.readLine();
header += line + " ";
} while (!line.matches(""));

if (header.matches(HTTPContinue)) {
// Connection's established. Wait for the next header
header = "";
bContinue = true;
} else if (header.matches(HTTPOk)) {
// HTTP Ok. Retreive the data
byteCount = parseContentLength(header);
header = "";
bContinue = false;
} else if (header.matches(HTTPSuccess)) {
// Some other non-error success code
// Try again to see what happens, or wait until the socket
// times out
header = "";
bContinue = true;
} else if (header.matches(HTTPRedirect)) {
// Shouldn't encounter this one, but if we do I have no idea
// how to handle it
bContinue = false;
throw new Exception("HTTP Redirect encountered:\r\n"
+ header);
} else if (header.matches(HTTPClientError)) {
// Client error, most likely a Server not Found (404) or
// Forbidden (403, bad credentials).
//header = "";
bContinue = false;
throw new Exception("HTTP Client Error encountered:\r\n"
+ header);
} else if (header.matches(HTTPServerError)) {
// Server errors. Internal Service Error (500) type errors
byteCount = parseContentLength(header);
char c[] = new char[byteCount];
rd.read(c, 0, byteCount);

//header = "";
bContinue = false;

throw new Exception("HTTP Server Error encountered:\r\n" + header + "\r\n" + String.valueOf(c));
} else {
throw new Exception("Unknown HTTP header:\r\n" + header);
}
}

if (byteCount != 0) {
// Read Data
char c[] = new char[byteCount];
rd.read(c, 0, byteCount);

DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

StringReader reader = new StringReader(String.valueOf(c)+ "\r\n");
InputSource source = new InputSource(reader);

Document doc = docBuilder.parse(source);
doc.getDocumentElement().normalize();

NodeList list = doc.getElementsByTagName(remoteProcedure + "Result");
if (list.getLength() > 0) {
result = list.item(0).getFirstChild().getNodeValue();
}
}
}

catch (Exception ex) {
System.out.println(ex.toString());
}

return result;
}

private int parseContentLength(String header) {
try {
int start = header.indexOf("Content-Length: ");
int end = header.indexOf(" ", start + 16);
String contentLine = header.substring(start, end);
contentLine = contentLine.replaceAll("^Content-Length: ", "");
contentLine = contentLine.trim();

return Integer.valueOf(contentLine).intValue();
} catch (Exception ex) {
return 0;
}
}
}

Also create a class called ParameterCollection which will hold the parameters to the web method invoked by the Java invoker:

public class ParameterCollection {
private String[][] _params;
int _index;
int _size = 10;

public ParameterCollection()
{
_params = new String[_size][2];
_index = 0;
}

public void Add(String param_name, String param_value)
{
if (_index >= _size)
{
IncreaseSize();
}

_params[_index][0] = param_name;
_params[_index][1] = param_value;

++ _index;
}

public String[] Get(int index) throws Exception
{
String[] retVal = new String[2];

if (index >= _index)
{
// Out of bounds
throw new Exception("Parameter Collection index is out of bounds: ParameterCollection.Get(" + index + ")");
}

retVal[0] = _params[index][0];
retVal[1] = _params[index][1];

return retVal;
}

public int size()
{
return _index;
}

private void IncreaseSize()
{
String[][] sOld = _params;
_size *= 2;

_params = new String[_size][2];

for (int i = 0; i < _size/2; ++i)
{
_params[i][0] = sOld[i][0];
_params[i][1] = sOld[i][1];
}
}
}


Next, after using the Visual Studio ASP.NET web service project wizard to create a web service project, add the following attribute to the class declaration of the class representing the web service:

[SoapDocumentService(RoutingStyle=SoapServiceRoutingStyle.RequestElement)]

The class declaration should look something like:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
[SoapDocumentService(RoutingStyle=SoapServiceRoutingStyle.RequestElement)]
public class Service1 : System.Web.Services.WebService


In the java class that will invoke the web service, create an instance of the WebServiceInvoker class:

WebServiceInvoker proxy = new WebServiceInvoker(webserviceservername, port, webserviceurl);

Create a list of parameters and populate appropriately:
ParameterCollection() oColl = new ParameterCollection();
oCall.Add(“parameter1”, “value1”);

Then use this as a parameter to the invokeRPC method of the web service proxy class:

Proxy.invokeRPC(“Name of web method to call”, oCall);

In IIS, make sure the ASP.NET web service accepts anonymous connections, and set which user the anonymous connections will use to access resources.