Skip to content

Commit 69d0721

Browse files
committed
add simple json patching method. working on net35 and Unity
1 parent 11c76cb commit 69d0721

File tree

9 files changed

+242
-38
lines changed

9 files changed

+242
-38
lines changed

Colyseus/Client.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
using WebSocketSharp;
1111
using MsgPack.Serialization;
1212

13-
using Newtonsoft.Json;
14-
using JsonDiffPatch;
13+
using Newtonsoft.Json.Linq;
14+
//using JsonDiffPatch;
1515

1616
namespace Colyseus
1717
{
@@ -144,15 +144,13 @@ void OnMessageHandler (object sender, WebSocketSharp.MessageEventArgs e)
144144

145145
} else if (code == Protocol.ROOM_STATE) {
146146
object state = message [2];
147-
147+
148148
room = (Room)this.rooms [roomId];
149-
room.state = Newtonsoft.Json.Linq.JToken.Parse (message [2].ToString ());
149+
room.state = JToken.Parse (message [2].ToString ());
150150

151151
} else if (code == Protocol.ROOM_STATE_PATCH) {
152-
PatchDocument patches = PatchDocument.Parse (message [2].ToString());
153-
154152
room = (Room) this.rooms [roomId];
155-
room.ApplyPatches(patches);
153+
room.ApplyPatches(JArray.Parse ( message [2].ToString() ));
156154

157155
} else if (code == Protocol.ROOM_DATA) {
158156
room = (Room) this.rooms [roomId];
@@ -161,7 +159,7 @@ void OnMessageHandler (object sender, WebSocketSharp.MessageEventArgs e)
161159

162160
this.OnMessage.Emit (this, new MessageEventArgs(room, message));
163161
}
164-
162+
165163
/// <summary>
166164
/// Request <see cref="Client"/> to join in a <see cref="Room"/>.
167165
/// </summary>

Colyseus/Colyseus.csproj

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<OutputType>Library</OutputType>
88
<RootNamespace>Colyseus</RootNamespace>
99
<AssemblyName>Colyseus</AssemblyName>
10-
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
10+
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
1111
</PropertyGroup>
1212
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1313
<DebugSymbols>true</DebugSymbols>
@@ -29,20 +29,14 @@
2929
</PropertyGroup>
3030
<ItemGroup>
3131
<Reference Include="System" />
32-
<Reference Include="MsgPack">
33-
<HintPath>packages\MsgPack.Cli.0.6.5\lib\net45\MsgPack.dll</HintPath>
34-
</Reference>
3532
<Reference Include="websocket-sharp">
3633
<HintPath>packages\WebSocketSharp.1.0.3-rc9\lib\websocket-sharp.dll</HintPath>
3734
</Reference>
38-
<Reference Include="Tavis.JsonPointer">
39-
<HintPath>packages\Tavis.JsonPointer.0.5.0\lib\portable-wp71+sl4+netcore45+net4\Tavis.JsonPointer.dll</HintPath>
40-
</Reference>
41-
<Reference Include="JsonDiffPatch">
42-
<HintPath>packages\JsonDiffPatch.1.0.35\lib\portable-net40+sl50+win+wp80+MonoAndroid10+xamarinios10+MonoTouch10\JsonDiffPatch.dll</HintPath>
35+
<Reference Include="MsgPack">
36+
<HintPath>packages\MsgPack.Cli.0.6.5\lib\net35-client\MsgPack.dll</HintPath>
4337
</Reference>
4438
<Reference Include="Newtonsoft.Json">
45-
<HintPath>packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
39+
<HintPath>packages\Newtonsoft.Json.7.0.1\lib\net35\Newtonsoft.Json.dll</HintPath>
4640
</Reference>
4741
</ItemGroup>
4842
<ItemGroup>
@@ -51,6 +45,8 @@
5145
<Compile Include="Room.cs" />
5246
<Compile Include="Client.cs" />
5347
<Compile Include="MessageEventArgs.cs" />
48+
<Compile Include="Patcher.cs" />
49+
<Compile Include="JsonPointer\JsonPointer.cs" />
5450
</ItemGroup>
5551
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
5652
<ItemGroup>

Colyseus/Example/MainWindow.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ void Room_OnUpdate (object sender, RoomUpdateEventArgs e)
3030
textview1.Buffer.Text += messages[i] + "\n";
3131
}
3232
} else {
33-
for (int i = 0; i < e.patches.Operations.Count; i++) {
34-
AddOperation operation = (AddOperation) e.patches.Operations [i];
35-
textview1.Buffer.Text += operation.Value + "\n";
33+
Console.WriteLine (e.patches);
34+
for (int i = 0; i < e.patches.Count; i++) {
35+
textview1.Buffer.Text += e.patches[i]["value"].ToString() + "\n";
3636
}
3737
}
3838
}

Colyseus/JsonPointer/JsonPointer.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Linq;
3+
using Newtonsoft.Json.Linq;
4+
5+
namespace Colyseus
6+
{
7+
public class JsonPointer
8+
{
9+
private readonly string[] _Tokens;
10+
11+
public JsonPointer(string pointer)
12+
{
13+
_Tokens = pointer.Split('/').Skip(1).Select(Decode).ToArray();
14+
}
15+
16+
private JsonPointer(string[] tokens)
17+
{
18+
_Tokens = tokens;
19+
}
20+
private string Decode(string token)
21+
{
22+
return Uri.UnescapeDataString(token).Replace("~1", "/").Replace("~0", "~");
23+
}
24+
25+
public bool IsNewPointer()
26+
{
27+
return _Tokens.Last() == "-";
28+
}
29+
30+
public JsonPointer ParentPointer
31+
{
32+
get
33+
{
34+
if (_Tokens.Length == 0) return null;
35+
return new JsonPointer(_Tokens.Take(_Tokens.Length-1).ToArray());
36+
}
37+
}
38+
39+
public JToken Find(JToken sample)
40+
{
41+
if (_Tokens.Length == 0)
42+
{
43+
return sample;
44+
}
45+
try
46+
{
47+
var pointer = sample;
48+
foreach (var token in _Tokens)
49+
{
50+
if (pointer is JArray)
51+
{
52+
pointer = pointer[Convert.ToInt32(token)];
53+
}
54+
else
55+
{
56+
pointer = pointer[token];
57+
if (pointer == null)
58+
{
59+
throw new ArgumentException("Cannot find " + token);
60+
}
61+
62+
}
63+
}
64+
return pointer;
65+
}
66+
catch (Exception ex)
67+
{
68+
throw new ArgumentException("Failed to dereference pointer",ex);
69+
}
70+
}
71+
72+
public override string ToString()
73+
{
74+
return "/" + String.Join("/", _Tokens);
75+
}
76+
}
77+
}

Colyseus/MessageEventArgs.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
using System;
2-
using JsonDiffPatch;
32

43
using Newtonsoft.Json.Linq;
54

65
namespace Colyseus
76
{
8-
// Class aliases
9-
using PatchDocument = JsonDiffPatch.PatchDocument;
10-
using JToken = Newtonsoft.Json.Linq.JToken;
117

128
/// <summary>
139
/// Representation of a message received from the server.
@@ -51,11 +47,11 @@ public class RoomUpdateEventArgs : EventArgs
5147
/// <summary>
5248
/// Patches applied to the <see cref="Room" /> state.
5349
/// </summary>
54-
public PatchDocument patches = null;
50+
public JArray patches = null;
5551

5652
/// <summary>
5753
/// </summary>
58-
public RoomUpdateEventArgs (Room room, JToken state, PatchDocument patches = null)
54+
public RoomUpdateEventArgs (Room room, JToken state, JArray patches = null)
5955
{
6056
this.room = room;
6157
this.state = state;

Colyseus/Patcher.cs

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Linq;
3+
using Newtonsoft.Json.Linq;
4+
5+
// Patcher implementation heavily based on:
6+
// - https://github.com/mcintyre321/JsonDiffPatch/ (e347344 - Sep 14, 2015)
7+
// - https://github.com/tavis-software/Tavis.JsonPointer (1403ff9 - Jan 11, 2015)
8+
9+
namespace Colyseus
10+
{
11+
public class Patcher
12+
{
13+
public Patcher ()
14+
{
15+
}
16+
17+
public void Patch (ref JToken document, JArray patches)
18+
{
19+
for (int i = 0; i < patches.Count; i++) {
20+
string operation = patches [i]["op"].ToString();
21+
JsonPointer pointer = new JsonPointer (patches [i] ["path"].ToString ());
22+
document = ApplyOperation (operation, pointer, patches[i], document);
23+
}
24+
}
25+
26+
protected JToken ApplyOperation (string operation, JsonPointer pointer, JToken patch, JToken target)
27+
{
28+
if (operation == "add") Add(pointer, patch["value"], target);
29+
else if (operation == "copy") Copy(new JsonPointer(patch["from"].ToString()), pointer, patch["value"], target);
30+
else if (operation == "move") Move(new JsonPointer(patch["from"].ToString()), pointer, patch["value"], target);
31+
else if (operation == "remove") Remove(pointer, patch["value"], target);
32+
else if (operation == "replace") target = Replace(pointer, patch["value"], target) ?? target;
33+
return target;
34+
}
35+
36+
protected JToken Replace(JsonPointer pointer, JToken value, JToken target)
37+
{
38+
var token = pointer.Find(target);
39+
if (token.Parent == null)
40+
{
41+
return value;
42+
}
43+
else
44+
{
45+
token.Replace(value);
46+
return null;
47+
}
48+
}
49+
50+
protected void Add(JsonPointer pointer, JToken value, JToken target)
51+
{
52+
JToken token = null;
53+
JObject parenttoken = null;
54+
var propertyName = pointer.ToString().Split('/').LastOrDefault();
55+
try
56+
{
57+
var parentArray = pointer.ParentPointer.Find(target) as JArray;
58+
59+
if (parentArray == null || propertyName == "-")
60+
{
61+
if (pointer.IsNewPointer())
62+
{
63+
var parentPointer = pointer.ParentPointer;
64+
token = parentPointer.Find(target) as JArray;
65+
}
66+
else
67+
{
68+
token = pointer.Find(target);
69+
}
70+
}
71+
else
72+
{
73+
parentArray.Insert(int.Parse(propertyName), value);
74+
return;
75+
}
76+
}
77+
catch (ArgumentException)
78+
{
79+
var parentPointer = pointer.ParentPointer;
80+
parenttoken = parentPointer.Find(target) as JObject;
81+
}
82+
83+
if (token == null && parenttoken != null)
84+
{
85+
parenttoken.Add(propertyName, value);
86+
}
87+
else if (token is JArray)
88+
{
89+
var array = token as JArray;
90+
91+
array.Add(value);
92+
}
93+
else if (token.Parent is JProperty)
94+
{
95+
var prop = token.Parent as JProperty;
96+
prop.Value = value;
97+
}
98+
}
99+
100+
101+
protected void Remove(JsonPointer pointer, JToken value, JToken target)
102+
{
103+
var token = pointer.Find(target);
104+
if (token.Parent is JProperty)
105+
{
106+
token.Parent.Remove();
107+
}
108+
else
109+
{
110+
token.Remove();
111+
}
112+
}
113+
114+
protected void Move(JsonPointer fromPointer, JsonPointer pointer, JToken value, JToken target)
115+
{
116+
if (pointer.ToString().StartsWith(fromPointer.ToString())) throw new ArgumentException("To path cannot be below from path");
117+
118+
var token = fromPointer.Find(target);
119+
Remove (fromPointer, value, target);
120+
Add(pointer, value, target);
121+
}
122+
123+
protected void Copy(JsonPointer fromPointer, JsonPointer pointer, JToken value, JToken target)
124+
{
125+
var token = fromPointer.Find(target); // Do I need to clone this?
126+
Add(pointer, value, target);
127+
}
128+
}
129+
}
130+

Colyseus/Room.cs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
using WebSocketSharp;
44
using Newtonsoft.Json.Linq;
5-
using JsonDiffPatch;
5+
//using JsonDiffPatch;
66

77
namespace Colyseus
88
{
@@ -19,6 +19,7 @@ public class Room
1919

2020
private int _id = 0;
2121
private JToken _state = null;
22+
private Patcher patcher;
2223

2324
/// <summary>
2425
/// Occurs when the <see cref="Client"/> successfully connects to the <see cref="Room"/>.
@@ -38,12 +39,12 @@ public class Room
3839
/// <summary>
3940
/// Occurs when server send patched state, before <see cref="OnUpdate"/>.
4041
/// </summary>
41-
public event EventHandler OnPatch;
42+
public event EventHandler<MessageEventArgs> OnPatch;
4243

4344
/// <summary>
4445
/// Occurs when server sends a message to this <see cref="Room"/>
4546
/// </summary>
46-
public event EventHandler OnData;
47+
public event EventHandler<MessageEventArgs> OnData;
4748

4849
/// <summary>
4950
/// Occurs after applying the patched state on this <see cref="Room"/>.
@@ -62,6 +63,7 @@ public Room (Client client, String name)
6263
{
6364
this.client = client;
6465
this.name = name;
66+
this.patcher = new Patcher ();
6567
}
6668

6769
/// <summary>
@@ -117,13 +119,15 @@ public void ReceiveData (object data)
117119
}
118120

119121
/// <summary>Internal usage, shouldn't be called.</summary>
120-
public void ApplyPatches (PatchDocument patches)
122+
public void ApplyPatches (JArray patches)
121123
{
122124
this.OnPatch.Emit (this, new MessageEventArgs(this, patches));
123125

124-
var patcher = new JsonPatcher();
125-
patcher.Patch(ref this._state, patches);
126+
this.patcher.Patch (ref this._state, patches);
126127

128+
// var patcher = new JsonPatcher();
129+
// patcher.Patch(ref this._state, patches);
130+
127131
this.OnUpdate.Emit (this, new RoomUpdateEventArgs(this, (JToken) this._state, patches));
128132
}
129133

0 commit comments

Comments
 (0)