Last active
December 15, 2015 19:29
-
-
Save Sephi-Chan/5312077 to your computer and use it in GitHub Desktop.
Chat server with Erlang.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(presence_server). | |
-behaviour(gen_server). | |
-export([ start_link/0 ]). | |
-export([ player_is_online/1, player_is_offline/1, online_players/0 ]). | |
-export([ init/1, handle_cast/2, handle_call/3, terminate/2, handle_info/2, code_change/3 ]). | |
start_link() -> | |
gen_server:start_link({ local, ?MODULE }, ?MODULE, [], []). | |
player_is_online(PlayerId) -> | |
gen_server:cast(?MODULE, { player_is_online, PlayerId }). | |
player_is_offline(PlayerId) -> | |
gen_server:cast(?MODULE, { player_is_offline, PlayerId }). | |
online_players() -> | |
gen_server:call(?MODULE, { online_players }). | |
init([]) -> | |
State = dict:new(), | |
{ ok, State }. | |
handle_cast({ player_is_online, PlayerId }, State) -> | |
NewState = dict:store(PlayerId, online, State), | |
{ noreply, NewState }; | |
handle_cast({ player_is_offline, PlayerId }, State) -> | |
NewState = dict:erase(PlayerId, State), | |
{ noreply, NewState }. | |
handle_call({ online_players }, _From, State) -> | |
Players = dict:fetch_keys(State), | |
{ reply, Players, State }. | |
terminate(_Reason, _State) -> | |
ok. | |
handle_info(_Info, State) -> | |
{ noreply, State }. | |
code_change(_OldVersion, State, _Extra) -> | |
{ ok, State }. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(seelies_server_websocket_handler). | |
-behaviour(cowboy_websocket_handler). | |
-export([ init/3, websocket_init/3, websocket_handle/3, websocket_info/3, websocket_terminate/3 ]). | |
-record(connection, { id, name = none }). | |
-include("../include/constants.hrl"). | |
init({ tcp, http }, _Request, _Options) -> | |
{ upgrade, protocol, cowboy_websocket }. | |
websocket_init(_TransportName, Request, _Options) -> | |
State = #connection{ id = erlang:make_ref() }, | |
{ ok, Request, State }. | |
websocket_handle({ text, Message }, Request, State) -> | |
{ _Type, _Reply, NewState } = parseAction(mochijson3:decode(Message), State), | |
{ ok, Request, NewState }. | |
websocket_info({ push_json, Json }, Request, State) -> | |
{ reply, { text, Json }, Request, State }; | |
websocket_info(_Info, Request, State) -> | |
{ ok, Request, State }. | |
websocket_terminate(_Reason, _Request, State) -> | |
case Name = State#connection.name of | |
none -> | |
ok; | |
_ -> | |
presence_server:player_is_offline(Name), | |
push_json({ p, l, { room } }, [ ?PLAYER_LEAVE, Name ]), | |
ok | |
end. | |
parseAction([ ?IDENTIFY, Name ], State) -> | |
case gproc:lookup_local_name({ connection, Name }) of | |
undefined -> | |
gproc:add_local_name({ connection, Name }), | |
gproc:add_local_property({ room }, Name), | |
presence_server:player_is_online(Name), | |
push_json([ ?CONNECTED, Name, presence_server:online_players() ]), | |
push_json({ p, l, { room } }, [ ?PLAYER_JOIN, Name ]), | |
{ json, ok, State#connection{ name = Name } }; | |
_ -> | |
push_json([ ?NAME_TAKEN ]), | |
{ json, ok, State } | |
end; | |
parseAction([ ?SEND_MESSAGE, Message ], State) -> | |
push_json({ p, l, { room } }, [ ?MESSAGE_RECEIVED, Message, State#connection.name ]), | |
{ json, ok, State }; | |
parseAction(_Message, State) -> | |
{ error, none, State }. | |
push_json(Content) -> | |
self() ! { push_json, mochijson3:encode(Content) }. | |
push_json(GprocKey, Content) -> | |
gproc:send(GprocKey, { push_json, mochijson3:encode(Content) }). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-record(seelies_players, { id, name, password }). | |
% Actions. | |
-define(IDENTIFY, 0). | |
-define(SEND_MESSAGE, 1). | |
% Responses. | |
-define(NAME_TAKEN, 0). | |
-define(CONNECTED, 1). | |
-define(PLAYER_JOIN, 2). | |
-define(MESSAGE_RECEIVED, 3). | |
-define(PLAYER_LEAVE, 4). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var socket = new WebSocket("ws://app.dev:7788"); | |
var actions = { IDENTIFY: 0, SEND_MESSAGE: 1 }; | |
var responses = { NAME_TAKEN: 0, CONNECTED: 1, JOIN: 2, MESSAGE_RECEIVED: 3, LEAVE: 4 }; | |
var callbacks = {}; | |
var me = null; | |
var $body = $('body'); | |
var $register = $body.find('#register'); | |
var $nameForm = $register.find('form'); | |
var $nameInput = $nameForm.find('input'); | |
var $nameTaken = $nameForm.find('.name_taken'); | |
var $nameBlank = $nameForm.find('.name_blank'); | |
var $room = $body.find('#room'); | |
var $players = $room.find('#players'); | |
var $messages = $room.find('#messages'); | |
var $messageForm = $room.find('form'); | |
var $messageInput = $messageForm.find('input'); | |
$nameForm.on('submit', function(event) { | |
event.preventDefault(); | |
var name = $nameInput.val(); | |
if (name.trim() == '') { | |
$nameBlank.removeClass('hidden'); | |
} | |
else { | |
var message = [ actions.IDENTIFY, $nameInput.val() ]; | |
socket.send(JSON.stringify(message)); | |
$nameBlank.addClass('hidden'); | |
} | |
}); | |
$messageForm.on('submit', function(event) { | |
event.preventDefault(); | |
var text = $messageInput.val().trim() | |
if (text != '') { | |
var message = [ actions.SEND_MESSAGE, text ]; | |
socket.send(JSON.stringify(message)); | |
$messageInput.val(''); | |
} | |
}); | |
callbacks[responses.NAME_TAKEN] = function(data) { | |
$nameTaken.removeClass("hidden"); | |
}; | |
callbacks[responses.CONNECTED] = function(data) { | |
me = data[1]; | |
var otherPlayers = data[2]; | |
$nameTaken.removeClass("hidden"); | |
$nameTaken.addClass("hidden"); | |
$register.addClass("hidden"); | |
$room.removeClass("hidden"); | |
$messageInput.focus(); | |
_(otherPlayers).each(function(name) { | |
if (name == me) { | |
$('<li><strong>' + name + '</strong></li>').appendTo($players); | |
} | |
else { | |
$('<li>' + name + '</li>').appendTo($players); | |
} | |
}); | |
}; | |
callbacks[responses.JOIN] = function(data) { | |
var name = data[1]; | |
if (name != me) { $('<li></li>', { text: name }).appendTo($players); } | |
$('<li>' + name + ' joined the room.</li>').prependTo($messages); | |
}; | |
callbacks[responses.MESSAGE_RECEIVED] = function(data) { | |
var message = data[1]; | |
var author = data[2]; | |
$('<li><strong>' + author + '</strong> <span>' + message + '</span></li>').prependTo($messages); | |
}; | |
callbacks[responses.LEAVE] = function(data) { | |
var name = data[1]; | |
$players.find('li').each(function() { | |
$player = $(this); | |
if ($player.text() == name) { $player.remove(); } | |
}); | |
$('<li>' + name + ' left the room.</li>').prependTo($messages); | |
}; | |
socket.onmessage = function(event) { | |
var data = JSON.parse(event.data); | |
var responseType = data[0]; | |
callbacks[responseType](data); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment