Skip to content

Instantly share code, notes, and snippets.

@trapped
Created July 17, 2014 00:32
Show Gist options
  • Save trapped/de3704881d8eeb6a3eaf to your computer and use it in GitHub Desktop.
Save trapped/de3704881d8eeb6a3eaf to your computer and use it in GitHub Desktop.
RotMG proof-of-concept server in Erlang
%% RotMG game module
-module(rotmgs).
-export([start/0, read/1, processpacket/2, send/2]).
-define(PORT, 2050).
-define(TCP_OPTIONS, [binary,
{packet, 0},
{active, false},
{backlog, 1024},
{reuseaddr, true}]).
-define(RC4R, [16#31, 16#1f, 16#80, 16#69, 16#14, 16#51, 16#c7, 16#1d, 16#09, 16#a1, 16#3a, 16#2a, 16#6e]).
-define(RC4S, [16#72, 16#c5, 16#58, 16#3c, 16#af, 16#b6, 16#81, 16#89, 16#95, 16#cd, 16#d7, 16#4b, 16#80]).
start() ->
io:format("Starting~n"),
crypto:start(),
listen().
listen() ->
{ok, Listener} = gen_tcp:listen(?PORT, ?TCP_OPTIONS),
io:format("Listening on ~b~n", [?PORT]),
accept(Listener).
accept(Listener) ->
{ok, Client} = gen_tcp:accept(Listener),
io:format("Accepted client~n"),
spawn(?MODULE, read, [Client]),
accept(Listener).
read(Client) ->
case gen_tcp:recv(Client, 4) of
{ok, Data} ->
<<Length:32/big-unsigned-integer>> = Data,
readpacket(Client, Length);
{error, _} ->
ok
end.
readpacket(Client, Length) ->
io:format("Packet length: ~b~n", [Length]),
case gen_tcp:recv(Client, Length - 4) of
{ok, Data} ->
spawn_link(?MODULE, processpacket, [Client, Data]);
{error, _} ->
ok
end,
read(Client).
processpacket(Client, Packet) ->
<<Ty, EData/binary>> = Packet,
io:format("Packet type: ~b~n", [Ty]),
Type =
case Ty of
13 ->
hello
end,
Data = crypto:rc4_encrypt(?RC4R, EData),
processpacket(Type, Client, Data).
processpacket(hello, Client, Data) ->
<<BVLn:16/big-unsigned-integer, BuildVersion:BVLn/binary,
GmId:32/big-unsigned-integer,
GLen:16/big-unsigned-integer, Guid:GLen/binary,
Rnd1:32/big-unsigned-integer,
PLen:16/big-unsigned-integer, Password:PLen/binary,
Rnd2:32/big-unsigned-integer,
SLen:16/big-unsigned-integer, Secret:SLen/binary,
KTim:32/big-unsigned-integer,
KLen:16/big-unsigned-integer, Key:KLen/binary,
MLen:32/big-unsigned-integer, MapInfo:MLen/binary,
OLn1:16/big-unsigned-integer, Obf1:OLn1/binary,
OLn2:16/big-unsigned-integer, Obf2:OLn2/binary,
OLn3:16/big-unsigned-integer, Obf3:OLn3/binary,
OLn4:16/big-unsigned-integer, Obf4:OLn4/binary,
OLn5:16/big-unsigned-integer, Obf5:OLn5/binary>> = Data,
io:format("Received HELLO: ~s~n", [BuildVersion]),
%failure(Client, 55, "Test error")
mapinfo(Client, 10, 10, "Nexus", "Nexuss", 0, 0, 1, true, true).
failure(Client, Id, LMessage) ->
Message = list_to_binary(LMessage),
MsgSize = byte_size(Message),
Base = <<0, MsgSize:16/big-unsigned-integer, Message/binary>>,
BaseSize = byte_size(Base) + 4,
Packet = <<BaseSize:32/big-unsigned-integer, Base/binary>>,
spawn_link(?MODULE, send, [Client, Packet]).
mapinfo(Client, Width, Height, Name, WorldName, Fp, Background, Difficulty, AllowTeleport, ShowDisplays) ->
LName = list_to_binary(Name),
LWorldName = list_to_binary(WorldName),
NLen = byte_size(LName),
WNLen = byte_size(LWorldName),
AllowTeleportB =
case AllowTeleport of
true ->
16#1;
false ->
16#0
end,
ShowDisplaysB =
case ShowDisplays of
true ->
16#1;
false ->
16#0
end,
Base = <<16#62,
Width:32/big-unsigned-integer,
Height:32/big-unsigned-integer,
NLen:16/big-unsigned-integer, LName:NLen/binary,
WNLen:16/big-unsigned-integer, LWorldName:WNLen/binary,
Fp:32/big-unsigned-integer,
Background:32/big-unsigned-integer,
Difficulty:32/big-unsigned-integer,
AllowTeleportB, ShowDisplaysB, 0:16, 0:16>>,
%CXLen:16/big-unsigned-integer, ClientXML:CXLen/binary,
%EXLen:16/big-unsigned-integer, ExtraXML:EXLen/binary>>,
BaseSize = byte_size(Base) + 4,
Packet = <<BaseSize:32/big-unsigned-integer, Base/binary>>,
spawn_link(?MODULE, send, [Client, Packet]).
send(Client, Data) ->
case gen_tcp:send(Client, Data) of
ok ->
ok;
{error, _} ->
ok
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment