1 | %% Copyright (C) 2016 Wildfire Games.
|
---|
2 | %% This file is part of 0 A.D.
|
---|
3 | %%
|
---|
4 | %% 0 A.D. is free software: you can redistribute it and/or modify
|
---|
5 | %% it under the terms of the GNU General Public License as published by
|
---|
6 | %% the Free Software Foundation, either version 2 of the License, or
|
---|
7 | %% (at your option) any later version.
|
---|
8 | %%
|
---|
9 | %% 0 A.D. is distributed in the hope that it will be useful,
|
---|
10 | %% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
11 | %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
12 | %% GNU General Public License for more details.
|
---|
13 | %%
|
---|
14 | %% You should have received a copy of the GNU General Public License
|
---|
15 | %% along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
---|
16 |
|
---|
17 | -module(mod_gamelist).
|
---|
18 |
|
---|
19 | -behaviour(gen_mod).
|
---|
20 |
|
---|
21 | % Hack to fix build. Ejabberd rewrote the module build system without leaving any
|
---|
22 | % instructions on how to upgrade. This is from Stack Overflow
|
---|
23 | % (http://stackoverflow.com/questions/32542102/ejabberd-debugging)
|
---|
24 | %TODO: Proper version checking
|
---|
25 | %-ifndef(LAGER).
|
---|
26 | %-define(LAGER, 1).
|
---|
27 | %-endif.
|
---|
28 | %-ifndef(NO_EXT_LIB).
|
---|
29 | %-define(NO_EXT_LIB, 1).
|
---|
30 | %-endif.
|
---|
31 |
|
---|
32 | %% Handy shortcuts to improve readability
|
---|
33 | -record(xmlelement, {name = "" :: string(),
|
---|
34 | attrs = [] :: [{string(), string()}],
|
---|
35 | children = [] :: [{xmlcdata, iodata()} | xmlelement()]}).
|
---|
36 | -type xmlelement() :: #xmlelement{}.
|
---|
37 | % TODO: Evaluate if we actually need dbentry
|
---|
38 | -record(dbentry, {key :: jid,
|
---|
39 | value :: xmlelement()}).
|
---|
40 |
|
---|
41 | -include("ejabberd.hrl").
|
---|
42 | -include("jlib.hrl").
|
---|
43 |
|
---|
44 | -export([start/2, stop/1, process_gamelist_iq/3]).
|
---|
45 |
|
---|
46 | -define(GAMELIST_NS, "jabber:iq:gamelist").
|
---|
47 |
|
---|
48 | start(Host, _Opts) ->
|
---|
49 | ?INFO_MSG("Starting mod_gamelist", []),
|
---|
50 | % Create a table named 'games' with the default options
|
---|
51 | mnesia:create_table(games,
|
---|
52 | [{attributes, record_info(fields, dbentry)}, {record_name, dbentry}]),
|
---|
53 | %% Make sure that the table is empty, as create_table won't overwrite an existing
|
---|
54 | %% table
|
---|
55 | mnesia:clear_table(games),
|
---|
56 | %% Handle all stanzas addressed to the server containing the jabberd:iq:gamelist
|
---|
57 | %% namespace with process_gamelist_iq/3 and queue pending stanzas. May be
|
---|
58 | %% interesting to experiment with using parallel execution instead of queued.
|
---|
59 | gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?GAMELIST_NS, ?MODULE,
|
---|
60 | process_gamelist_iq, one_queue).
|
---|
61 |
|
---|
62 | stop(Host) ->
|
---|
63 | ?INFO_MSG("Stopping mod_gamelist", []),
|
---|
64 | mnesia:delete_table(games),
|
---|
65 | gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?GAMELIST_NS).
|
---|
66 |
|
---|
67 | % Handle gamelist IQ stanzas
|
---|
68 | process_gamelist_iq(From, To, IQ) ->
|
---|
69 | ?INFO_MSG("Gamelist stanza received from ~p: ~n ~p ~n", [From, IQ]),
|
---|
70 | % TODO: Add a checked room field to the message, keep per room databases
|
---|
71 | case IQ#iq.type of
|
---|
72 | set ->
|
---|
73 | % Find and extract the command
|
---|
74 | CommandXml = lists:keyfind("command", 2, IQ#iq.sub_el#xmlelement.children),
|
---|
75 | [{xmlcdata, Command}] = CommandXml#xmlelement.children,
|
---|
76 | ?INFO_MSG("Command: ~p~n", [Command]),
|
---|
77 | % TODO: Something about changestate
|
---|
78 | case Command of
|
---|
79 | <<"register">> ->
|
---|
80 | % Extract game
|
---|
81 | Game = lists:keyfind("game", 2, IQ#iq.sub_el#xmlelement.children),
|
---|
82 | % Get host's (sender's) IP so that client know who to connect to
|
---|
83 | Origin = ejabberd_sm:get_user_ip(From#jid.user, From#jid.server, From#jid.resource),
|
---|
84 | HostIPString = inet_parse:ntoa(element(1, Origin)),
|
---|
85 | % Merge in the IP
|
---|
86 | MergedGameProperties = lists:keyreplace("ip", 1, Game#xmlelement.attrs, {"ip", HostIPString}),
|
---|
87 | MergedGame = Game#xmlelement{attrs = MergedGameProperties},
|
---|
88 | ?INFO_MSG("Attempting to register game: ~p~nFrom: ~p", [MergedGame, From]),
|
---|
89 | % Save the game to the database
|
---|
90 | mnesia:dirty_write(games, {dbentry, From, MergedGame}),
|
---|
91 | % TODO: Multicast new gamelist
|
---|
92 | ?INFO_MSG("'dev' users: ~p", [p1_fsm:sync_send_all_state_event(mnesia:dirty_read(muc_online_room, {"dev", "localhost"}), {get_disco_item, From, "en"}, 100)]),%mod_muc_admin:get_room_occupants("dev", "localhost")]),
|
---|
93 | Room = #jid{user = "dev", server = "localhost", resource = "", luser = "dev", lserver = "localhost", lresource = ""},
|
---|
94 | ejabberd_router:route(To, Room, jlib:iq_to_xml(get_gamelist_result_iq())),
|
---|
95 | % Aknowledge registration
|
---|
96 | IQ#iq{type = result, sub_el = []};
|
---|
97 | <<"unregister">> ->
|
---|
98 | ?INFO_MSG("Attempting to unregister game...", []),
|
---|
99 | mnesia:dirty_delete(games, From),
|
---|
100 | IQ#iq{type = result, sub_el = []};
|
---|
101 | _Else ->
|
---|
102 | IQ#iq{type = error, sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]}
|
---|
103 | end;
|
---|
104 | get ->
|
---|
105 | % Dump database
|
---|
106 | ?INFO_MSG("Attempting to send game listings...", []),
|
---|
107 | get_gamelist_result_iq();
|
---|
108 | _Else ->
|
---|
109 | IQ#iq{type = error, sub_el = [IQ#iq.sub_el, ?ERR_NOT_ACCEPTABLE]}
|
---|
110 | end.
|
---|
111 |
|
---|
112 | get_gamelist_result_iq() ->
|
---|
113 | %% Create a Query List Comprehension handle for the database, and
|
---|
114 | %% evaluate it to get all the database contents
|
---|
115 | {atomic, Games} = mnesia:transaction(fun() -> qlc:eval(mnesia:table(games)) end),
|
---|
116 | ?INFO_MSG("Recieved: ~p from DB.~n", [Games]),
|
---|
117 | GamesXml = lists:map(fun(Game) -> Game#dbentry.value end, Games),
|
---|
118 | SubEl = #xmlelement{name = <<"query">>, attrs = [{<<"xmlns">>, ?GAMELIST_NS}], children = GamesXml},
|
---|
119 | #iq{type = result, xmlns = ?GAMELIST_NS, sub_el = [SubEl]}.
|
---|