Correct prev

master
Alexander Markevych 2012-04-23 07:47:27 +03:00
parent ad7e141ea5
commit d24c9061a4
14 changed files with 1528 additions and 0 deletions

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.kpf
*.beam
*.o
*.so
build/*
^build$
Emakefile
coverage
coverage/*
erl_crash.dump
priv/
test/logs/
ebin/*.beam
logs/*
ebin/test.erl

16
LICENSE Normal file
View File

@ -0,0 +1,16 @@
Copyright (c) 2012 Alexander Markevych and individual contributors
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.

41
Makefile Normal file
View File

@ -0,0 +1,41 @@
ERL ?= erl
ERLC = erlc
EBIN_DIRS := $(wildcard deps/*/ebin)
.PHONY: rel deps
all: deps compile
compile: deps
@./rebar compile
deps:
@./rebar get-deps
@./rebar check-deps
clean:
@./rebar clean
realclean: clean
@./rebar delete-deps
tests:
@./rebar skip_deps=true eunit
rel: deps
@./rebar compile generate
doc:
rebar skip_deps=true doc
console:
@erl -pa deps/*/ebin deps/*/include ebin include -boot start_sasl
analyze: checkplt
@./rebar skip_deps=true dialyze
buildplt:
@./rebar skip_deps=true build-plt
checkplt: buildplt
@./rebar skip_deps=true check-plt

19
README.md Normal file
View File

@ -0,0 +1,19 @@
This project is to run a Minetest Server using Erlang, mainly hoping
to improve server quality and stability.
In time this project will allow hundreds of players connected to one server,
rather than the current technical limitations of the minetestserver program
provided by celeron55's minetest GitHub repo.
This project should be under GPL V2 (or later.)
=======================================
This project is still being worked out, until a working server module is completed.
=======================================
The code is worked and created upon by Markevych (hummermania) Alexander.
Look at https://github.com/hummermania/
The game, Minetest is worked and maintained by Celeron55, aka, Perttu Ahola.
The repo for Minetest is https://github.com/celeron55/minetest and the
extended version of it can be found here: http://github.com/celeron55/minetest_game

9
ebin/erlmines.app Normal file
View File

@ -0,0 +1,9 @@
{application, erlmines,
[{description, "Minetest server written on Erlang"},
{vsn, "0.0.0"},
{modules, [erlmines,erlmines_app,erlmines_sup,clientserver]},
{registered, [erlmines]},
{applications, [kernel, stdlib]},
{mod, {erlmines_app, []}},
{start_phases, []}
]}.

521
include/connection.hrl Normal file
View File

@ -0,0 +1,521 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-define(U8, 8/unsigned-little-integer).
-define(U16, 16/unsigned-little-integer).
-define(U32, 32/unsigned-little-integer).
-record(buffered_packet, {
data, % Data of the packet, including headers
time, % Seconds from buffering the packet or re-sending
totaltime, % Seconds from buffering the packet
address % Sender or destination
}).
-record(original_packet, {
data,
address
}).
-record(reliable_packet, {
data
}).
-record(incoming_split_packet, {
% Key is chunk number, value is data without headers
chunks,
chunk_count,
time, % Seconds from adding
reliable % If true, isn't deleted on timeout
}).
-define(PROTOCOL_VERSION, 7).
-define(PROTOCOL_ID, 16#4f457403).
-define(PASSWORD_SIZE,28). %% Maximum password length. Allows for base64-encoded SHA-1 (27+\0).
-define(TEXTURENAME_ALLOWED_CHARS,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.").
%% === NOTES ===
%% A packet is sent through a channel to a peer with a basic header:
%% TODO: Should we have a receiver_peer_id also?
%% Header (7 bytes):
%% [0] u32 protocol_id
%% [4] u16 sender_peer_id
%% [6] u8 channel
%% sender_peer_id:
%% Unique to each peer.
%% value 0 is reserved for making new connections
%% value 1 is reserved for server
%% channel:
%% The lower the number, the higher the priority is.
%% Only channels 0, 1 and 2 exist.
-define(BASE_HEADER_SIZE, 7).
-define(PEER_ID_INEXISTENT, 0).
-define(PEER_ID_SERVER, 1).
-define(CHANNEL_COUNT, 3).
%% Packet types:
%% CONTROL: This is a packet used by the protocol.
%% - When this is processed, nothing is handed to the user.
%% Header (2 byte):
%% [0] u8 type
%% [1] u8 controltype
%% controltype and data description:
%% CONTROLTYPE_ACK
%% [2] u16 seqnum
%% CONTROLTYPE_SET_PEER_ID
%% [2] u16 peer_id_new
%% CONTROLTYPE_PING
%% - There is no actual reply, but this can be sent in a reliable
%% packet to get a reply
%% CONTROLTYPE_DISCO
-define(TYPE_CONTROL, 0).
-define(CONTROLTYPE_ACK, 0).
-define(CONTROLTYPE_SET_PEER_ID, 1).
-define(CONTROLTYPE_PING, 2).
-define(CONTROLTYPE_DISCO, 3).
%% ORIGINAL: This is a plain packet with no control and no error checking at all.
%% - When this is processed, it is directly handed to the user.
%% Header (1 byte):
%% [0] u8 type
-define(TYPE_ORIGINAL, 1).
-define(ORIGINAL_HEADER_SIZE, 1).
%% SPLIT: These are sequences of packets forming one bigger piece of data.
%% - When processed and all the packet_nums 0...packet_count-1 are
%% present (this should be buffered), the resulting data shall be
%% directly handed to the user.
%% - If the data fails to come up in a reasonable time, the buffer shall
%% be silently discarded.
%% - These can be sent as-is or atop of a RELIABLE packet stream.
%% Header (7 bytes):
%% [0] u8 type
%% [1] u16 seqnum
%% [3] u16 chunk_count
%% [5] u16 chunk_num
-define(TYPE_SPLIT, 2).
%% RELIABLE: Delivery of all RELIABLE packets shall be forced by ACKs,
%% and they shall be delivered in the same order as sent. This is done
%% with a buffer in the receiving and transmitting end.
%% - When this is processed, the contents of each packet is recursively
%% processed as packets.
%% Header (3 bytes):
%% [0] u8 type
%% [1] u16 seqnum
-define(TYPE_RELIABLE, 3).
-define(RELIABLE_HEADER_SIZE, 3).
%%#define SEQNUM_INITIAL 0x10
-define(SEQNUM_INITIAL, 65500).
-define(SEQNUM_MAX, 65535).
%% =================================
%% TOSERVER command group
%% =================================
-define(TOSERVER_INIT,16#10).
%% Sent first after connected.
%%
%% [0] u16 TOSERVER_INIT
%% [2] u8 SER_FMT_VER_HIGHEST
%% [3] u8[20] player_name
%% [23] u8[28] password (new in some version)
%% [51] u16 client network protocol version (new in some version)
-define(TOSERVER_INIT2,16#11).
%% Sent as an ACK for TOCLIENT_INIT.
%% After this, the server can send data.
%%
%% [0] u16 TOSERVER_INIT2
-define(TOSERVER_GETBLOCK, 16#20). %% Obsolete
-define(TOSERVER_ADDNODE, 16#21). %% Obsolete
-define(TOSERVER_REMOVENODE, 16#22). %% Obsolete
-define(TOSERVER_PLAYERPOS, 16#23).
%% [0] u16 command
%% [2] v3s32 position*100
%% [2+12] v3s32 speed*100
%% [2+12+12] s32 pitch*100
%% [2+12+12+4] s32 yaw*100
-define(TOSERVER_GOTBLOCKS,16#24).
%% [0] u16 command
%% [2] u8 count
%% [3] v3s16 pos_0
%% [3+6] v3s16 pos_1
%% ...
-define(TOSERVER_DELETEDBLOCKS, 16#25).
%% [0] u16 command
%% [2] u8 count
%% [3] v3s16 pos_0
%% [3+6] v3s16 pos_1
%% ...
-define(TOSERVER_ADDNODE_FROM_INVENTORY, 16#26). %% Obsolete
%% [0] u16 command
%% [2] v3s16 pos
%% [8] u16 i
-define(TOSERVER_CLICK_OBJECT, 16#27). %% Obsolete
%% length: 13
%% [0] u16 command
%% [2] u8 button (0=left, 1=right)
%% [3] v3s16 blockpos
%% [9] s16 id
%% [11] u16 item
-define(TOSERVER_GROUND_ACTION, 16#28). %% Obsolete
%% length: 17
%% [0] u16 command
%% [2] u8 action
%% [3] v3s16 nodepos_undersurface
% [9] v3s16 nodepos_abovesurface
% [15] u16 item
% actions:
% 0: start digging (from undersurface)
% 1: place block (to abovesurface)
% 2: stop digging (all parameters ignored)
% 3: digging completed
-define(TOSERVER_RELEASE, 16#29). %% Obsolete
% (oops, there is some gap here)
-define(TOSERVER_SIGNTEXT, 16#30). %% Old signs, obsolete
% u16 command
% v3s16 blockpos
% s16 id
% u16 textlen
% textdata
-define(TOSERVER_INVENTORY_ACTION, 16#31).
% See InventoryAction in inventory.h
-define(TOSERVER_CHAT_MESSAGE, 16#32).
%% u16 command
%% u16 length
%% wstring message
-define(TOSERVER_SIGNNODETEXT, 16#33).
%% u16 command
%% v3s16 p
%% u16 textlen
%% textdata
-define(TOSERVER_CLICK_ACTIVEOBJECT, 16#34). % Obsolete
%% length: 7
%% [0] u16 command
%% [2] u8 button (0=left, 1=right)
%% [3] u16 id
%% [5] u16 item
-define(TOSERVER_DAMAGE, 16#35).
%% u16 command
%% u8 amount
-define(TOSERVER_PASSWORD, 16#36).
%% Sent to change password.
%% [0] u16 TOSERVER_PASSWORD
%% [2] u8[28] old password
%% [30] u8[28] new password
-define(TOSERVER_PLAYERITEM, 16#37).
%% Sent to change selected item.
%% [0] u16 TOSERVER_PLAYERITEM
%% [2] u16 item
-define(TOSERVER_RESPAWN,16#38).
%% u16 TOSERVER_RESPAWN
-define(TOSERVER_INTERACT, 16#39).
%% [0] u16 command
%% [2] u8 action
%% [3] u16 item
%% [5] u32 length of the next item
%% [9] serialized PointedThing
%% actions:
%% 0: start digging (from undersurface) or use
%% 1: stop digging (all parameters ignored)
%% 2: digging completed
%% 3: place block or item (to abovesurface)
%% 4: use item
%% (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
-define(TOSERVER_REQUEST_TEXTURES, 16#40).
%% u16 command
%% u16 number of textures requested
%% for each texture {
%% u16 length of name
%% string name
%% }
%% =================================
%% TOCLIENT command group
%% =================================
-define(TOCLIENT_INIT, 16#10).
%% Server's reply to TOSERVER_INIT.
%% Sent second after connected.
%%
%% [0] u16 TOSERVER_INIT
%% [2] u8 deployed version
%% [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
%% [12] u64 map seed (new as of 2011-02-27)
%%
%% NOTE: The position in here is deprecated; position is
%% explicitly sent afterwards
-define(TOCLIENT_BLOCKDATA, 16#20). %%TODO: Multiple blocks
-define(TOCLIENT_ADDNODE, 16#21).
-define(TOCLIENT_REMOVENODE, 16#22).
-define(TOCLIENT_PLAYERPOS, 16#23). %% Obsolete
%% [0] u16 command
%% // Followed by an arbitary number of these:
%% // Number is determined from packet length.
%% [N] u16 peer_id
%% [N+2] v3s32 position*100
%% [N+2+12] v3s32 speed*100
%% [N+2+12+12] s32 pitch*100
%% [N+2+12+12+4] s32 yaw*100
-define(TOCLIENT_PLAYERINFO, 16#24). %% Obsolete
%% [0] u16 command
%% // Followed by an arbitary number of these:
%% // Number is determined from packet length.
%% [N] u16 peer_id
%% [N] char[20] name
-define(TOCLIENT_OPT_BLOCK_NOT_FOUND, 16#25). %% Obsolete
-define(TOCLIENT_SECTORMETA, 16#26). %% Obsolete
%% [0] u16 command
%% [2] u8 sector count
%% [3...] v2s16 pos + sector metadata
-define(TOCLIENT_INVENTORY, 16#27).
%% [0] u16 command
%% [2] serialized inventory
-define(TOCLIENT_OBJECTDATA, 16#28) %% Obsolete
%% Sent as unreliable.
%%
%% u16 command
%% u16 number of player positions
%% for each player:
%% u16 peer_id
%% v3s32 position*100
%% v3s32 speed*100
%% s32 pitch*100
%% s32 yaw*100
%% u16 count of blocks
%% for each block:
%% v3s16 blockpos
%% block objects
-define(TOCLIENT_TIME_OF_DAY, 16#29).
%% u16 command
%% u16 time (0-23999)
%% (oops, there is some gap here)
-define(TOCLIENT_CHAT_MESSAGE, 16#30).
%% u16 command
%% u16 length
%% wstring message
-define(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, 16#31).
%% u16 command
%% u16 count of removed objects
%% for all removed objects {
%% u16 id
%% }
%% u16 count of added objects
%% for all added objects {
%% u16 id
%% u8 type
%% u32 initialization data length
%% string initialization data
%% }
-define(TOCLIENT_ACTIVE_OBJECT_MESSAGES, 16#32).
%% u16 command
%% for all objects
%% {
%% u16 id
%% u16 message length
%% string message
%% }
-define(TOCLIENT_HP, 16#33).
%% u16 command
%% u8 hp
-define(TOCLIENT_MOVE_PLAYER, 16#34).
%% u16 command
%% v3f1000 player position
%% f1000 player pitch
%% f1000 player yaw
-define(TOCLIENT_ACCESS_DENIED, 16#35).
%% u16 command
%% u16 reason_length
%% wstring reason
-define(TOCLIENT_PLAYERITEM, 16#36)
%% u16 command
%% u16 count of player items
%% for all player items {
%% u16 peer id
%% u16 length of serialized item
%% string serialized item
%% }
-define(TOCLIENT_DEATHSCREEN, 16#37).
%% u16 command
%% u8 bool set camera point target
%% v3f1000 camera point target (to point the death cause or whatever)
-define(TOCLIENT_TEXTURES, 16#38).
%% u16 command
%% u16 total number of texture bunches
%% u16 index of this bunch
%% u32 number of textures in this bunch
%% for each texture {
%% u16 length of name
%% string name
%% u32 length of data
%% data
%% }
-define(TOCLIENT_TOOLDEF, 16#39).
%% u16 command
%% u32 length of the next item
%% serialized ToolDefManager
-define(TOCLIENT_NODEDEF, 16#3a).
%% u16 command
%% u32 length of the next item
%% serialized NodeDefManager
-define(TOCLIENT_CRAFTITEMDEF, 16#3b).
%% u16 command
%% u32 length of the next item
%% serialized CraftiItemDefManager
-define(TOCLIENT_ANNOUNCE_TEXTURES, 16#3c).
%% u16 command
%% u32 number of textures
%% for each texture {
%% u16 length of name
%% string name
%% u16 length of sha1_digest
%% string sha1_digest
%% }
-define(TOCLIENT_ITEMDEF, 16#3d).
%% u16 command
%% u32 length of next item
%% serialized ItemDefManager

427
src/clientserver.erl Executable file
View File

@ -0,0 +1,427 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(clientserver).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-export([init/0]).
init() ->
ToClientCommand = gb_sets:from_list([
{toclient_init, 16#10},
%
% Server's reply to TOSERVER_INIT.
% Sent second after connected.
%
% [0] u16 TOSERVER_INIT
% [2] u8 deployed version
% [3] v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
% [12] u64 map seed (new as of 2011-02-27)
%
% NOTE: The position in here is deprecated; position is
% explicitly sent afterwards
{ toclient_blockdata, 16#20}, % TODO: Multiple blocks
{ toclient_addnode, 16#21},
{ toclient_removenode, 16#22},
{ toclient_playerpos, 16#23}, % Obsolete
%
% [0] u16 command
% // Followed by an arbitary number of these:
% // Number is determined from packet length.
% [N] u16 peer_id
% [N+2] v3s32 position*100
% [N+2+12] v3s32 speed*100
% [N+2+12+12] s32 pitch*100
% [N+2+12+12+4] s32 yaw*100
{ toclient_playerinfo, 16#24}, % Obsolete
%
% [0] u16 command
% // Followed by an arbitary number of these:
% // Number is determined from packet length.
% [N] u16 peer_id
% [N] char[20] name
%
{ toclient_opt_block_not_found, 16#25}, % Obsolete
{ toclient_sectormeta, 16#26}, % Obsolete
%
% [0] u16 command
% [2] u8 sector count
% [3...] v2s16 pos + sector metadata
{ toclient_inventory, 16#27},
%
% [0] u16 command
% [2] serialized inventory
%
{ toclient_objectdata, 16#28}, % Obsolete
%
% Sent as unreliable.
%
% u16 command
% u16 number of player positions
% for each player:
% u16 peer_id
% v3s32 position*100
% v3s32 speed*100
% s32 pitch*100
% s32 yaw*100
% u16 count of blocks
% for each block:
% v3s16 blockpos
% block objects
%
{ toclient_time_of_day, 16#29},
%
% u16 command
% u16 time (0-23999)
% Added in a later version:
% f1000 time_speed
%
% (oops, there is some gap here)
{ toclient_chat_message, 16#30},
%
% u16 command
% u16 length
% wstring message
%
{ toclient_active_object_remove_add, 16#31},
%
% u16 command
% u16 count of removed objects
% for all removed objects {
% u16 id
% }
% u16 count of added objects
% for all added objects {
% u16 id
% u8 type
% u32 initialization data length
% string initialization data
% }
{ toclient_active_object_messages, 16#32},
%
% u16 command
% for all objects
% {
% u16 id
% u16 message length
% string message
% }
%
{ toclient_hp, 16#33},
%
% u16 command
% u8 hp
%
{ toclient_move_player, 16#34},
%
% u16 command
% v3f1000 player position
% f1000 player pitch
% f1000 player yaw
%
{ toclient_access_denied, 16#35},
%
% u16 command
% u16 reason_length
% wstring reason
%
{ toclient_playeritem, 16#36},
%
% u16 command
% u16 count of player items
% for all player items {
% u16 peer id
% u16 length of serialized item
% string serialized item
% }
%
{ toclient_deathscreen, 16#37},
%
% u16 command
% u8 bool set camera point target
% v3f1000 camera point target (to point the death cause or whatever)
%
{ toclient_media, 16#38},
%
% u16 command
% u16 total number of texture bunches
% u16 index of this bunch
% u32 number of files in this bunch
% for each file {
% u16 length of name
% string name
% u32 length of data
% data
% }
%
{ toclient_tooldef, 16#39},
%
% u16 command
% u32 length of the next item
% serialized ToolDefManager
%
{ toclient_nodedef, 16#3a},
%
% u16 command
% u32 length of the next item
% serialized NodeDefManager
%
{ toclient_craftitemdef, 16#3b},
%
% u16 command
% u32 length of the next item
% serialized CraftiItemDefManager
%
{ toclient_announce_media, 16#3c},
%
% u16 command
% u32 number of files
% for each texture {
% u16 length of name
% string name
% u16 length of sha1_digest
% string sha1_digest
% }
%
{ toclient_itemdef, 16#3d},
%
% u16 command
% u32 length of next item
% serialized ItemDefManager
%
{ toclient_play_sound, 16#3f},
%
% u16 command
% s32 sound_id
% u16 len
% u8[len] sound name
% s32 gain*1000
% u8 type (0=local, 1=positional, 2=object)
% s32[3] pos_nodes*10000
% u16 object_id
% u8 loop (bool)
%
{ toclient_stop_sound, 16#40}
%
% u16 command
% s32 sound_id
%
]),
ToServerCommand = gb_sets:from_list([
{ toserver_init, 16#10},
%
% Sent first after connected.
%
% [0] u16 TOSERVER_INIT
% [2] u8 SER_FMT_VER_HIGHEST
% [3] u8[20] player_name
% [23] u8[28] password (new in some version)
% [51] u16 client network protocol version (new in some version)
%
{ toserver_init2, 16#11},
%
% Sent as an ACK for TOCLIENT_INIT.
% After this, the server can send data.
%
% [0] u16 TOSERVER_INIT2
%
{ toserver_getblock, 16#20}, % Obsolete
{ toserver_addnode, 16#21}, % Obsolete
{ toserver_removenode, 16#22}, % Obsolete
{ toserver_playerpos, 16#23},
%
% [0] u16 command
% [2] v3s32 position*100
% [2+12] v3s32 speed*100
% [2+12+12] s32 pitch*100
% [2+12+12+4] s32 yaw*100
%
{ toserver_gotblocks, 16#24},
%
% [0] u16 command
% [2] u8 count
% [3] v3s16 pos_0
% [3+6] v3s16 pos_1
% ...
%
{ toserver_deletedblocks, 16#25},
%
% [0] u16 command
% [2] u8 count
% [3] v3s16 pos_0
% [3+6] v3s16 pos_1
% ...
%
{ toserver_addnode_from_inventory, 16#26}, % Obsolete
%
% [0] u16 command
% [2] v3s16 pos
% [8] u16 i
%
{ toserver_click_object, 16#27}, % Obsolete
%
% length: 13
% [0] u16 command
% [2] u8 button (0=left, 1=right)
% [3] v3s16 blockpos
% [9] s16 id
% [11] u16 item
%
{ toserver_ground_action, 16#28}, % Obsolete
%
% length: 17
% [0] u16 command
% [2] u8 action
% [3] v3s16 nodepos_undersurface
% [9] v3s16 nodepos_abovesurface
% [15] u16 item
% actions:
% 0: start digging (from undersurface)
% 1: place block (to abovesurface)
% 2: stop digging (all parameters ignored)
% 3: digging completed
%
{ toserver_release, 16#29}, % Obsolete
% (oops, there is some gap here)
{ toserver_signtext, 16#30}, % Old signs, obsolete
%
% u16 command
% v3s16 blockpos
% s16 id
% u16 textlen
% textdata
%
{ toserver_inventory_action, 16#31},
%
% See InventoryAction in inventory.h
%
{ toserver_chat_message, 16#32},
%
% u16 command
% u16 length
% wstring message
%
{ toserver_signnodetext, 16#33},
%
% u16 command
% v3s16 p
% u16 textlen
% textdata
%
{ toserver_click_activeobject, 16#34}, % Obsolete
%
% length: 7
% [0] u16 command
% [2] u8 button (0=left, 1=right)
% [3] u16 id
% [5] u16 item
%
{ toserver_damage, 16#35},
%
% u16 command
% u8 amount
%
{ toserver_password, 16#36},
%
% Sent to change password.
%
% [0] u16 TOSERVER_PASSWORD
% [2] u8[28] old password
% [30] u8[28] new password
%
{ toserver_playeritem, 16#37},
%
% Sent to change selected item.
%
% [0] u16 TOSERVER_PLAYERITEM
% [2] u16 item
%
{ toserver_respawn, 16#38},
%
% u16 TOSERVER_RESPAWN
%
{ toserver_interact, 16#39},
%
% [0] u16 command
% [2] u8 action
% [3] u16 item
% [5] u32 length of the next item
% [9] serialized PointedThing
% actions:
% 0: start digging (from undersurface) or use
% 1: stop digging (all parameters ignored)
% 2: digging completed
% 3: place block or item (to abovesurface)
% 4: use item
%
% (Obsoletes TOSERVER_GROUND_ACTION and TOSERVER_CLICK_ACTIVEOBJECT.)
%
{ toserver_removed_sounds, 16#3a},
%
% u16 command
% u16 len
% s32[len] sound_id
%
{ toserver_request_media, 16#40}
%
% u16 command
% u16 number of files requested
% for each file {
% u16 length of name
% string name
% }
%
]),
[ToClientCommand, ToServerCommand].

View File

@ -0,0 +1,88 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(connection).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-include("connection.hrl").
-export([makePacket/5, makeSplitPacket/3, test/0]).
ceiling(X) ->
T = trunc(X),
case X - T == 0 of
true -> T;
false -> T + 1
end.
readPeerId(PacketData) ->
<<_P:?U32,SenderPeerId:?U16,_Other/binary>> = PacketData,
SenderPeerId.
readChannel(PacketData) ->
<<_P:?U32,_S:?U16,Channel:?U8,_Other/binary>> = PacketData,
Channel.
% This adds the base headers to the data and makes a packet out of it
makePacket(Address, Data, ProtocolId,SenderPeerId,Channel)->
#buffered_packet{
address=Address,
data = <<ProtocolId:?U32,SenderPeerId:?U16,Channel:?U8,Data/binary>>
}.
% Add the TYPE_ORIGINAL header to the data
makeOriginalPacket(Data) ->
#original_packet{ data = <<?TYPE_ORIGINAL:?U8,Data/binary>> }.
% Split data in chunks and add TYPE_SPLIT headers to them
makeSplitPacket(Data,ChunkSizeMax,SeqNum) ->
ChunkHeaderSize = 7,
MaxDataSize = ChunkSizeMax - ChunkHeaderSize,
ChunkCount=ceiling(byte_size(Data)/MaxDataSize),
Chunks = makeSplitPacketList(MaxDataSize, SeqNum, ChunkCount, 0, Data),
Chunks.
makeSplitPacketList(MaxDataSize, SeqNum, ChunkCount, ChunkCount, Data) ->[];
makeSplitPacketList(MaxDataSize, SeqNum, ChunkCount, ChunkNum, Data) ->
DataSize = byte_size(Data),
ChunkDataSize = case MaxDataSize - DataSize < 0 of
true -> MaxDataSize;
false -> DataSize
end,
<<ChunkData:ChunkDataSize/binary,Tile/binary>> = Data,
%%ChunkNum1 = ChunkNum+1,
[<<?TYPE_SPLIT:?U8, SeqNum:?U16, ChunkCount:?U16, ChunkNum:?U16,ChunkData/binary>>|makeSplitPacketList(MaxDataSize,SeqNum,ChunkCount,ChunkNum+1,Tile)].
test() ->
makeSplitPacket(list_to_binary([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]),9,65500).
% Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet
% Increments split_seqnum if a split packet is made
makeAutoSplitPacket(Data,ChunkSizeMax,SplitSeqNum) ->
todo.
% core::list<SharedBuffer<u8> > makeAutoSplitPacket(
% SharedBuffer<u8> data,
% u32 chunksize_max,
% u16 &split_seqnum)
%{
% u32 original_header_size = 1;
% core::list<SharedBuffer<u8> > list;
% if(data.getSize() + original_header_size > chunksize_max)
% {
% list = makeSplitPacket(data, chunksize_max, split_seqnum);
% split_seqnum++;
% return list;
% }
% else
% {
% list.push_back(makeOriginalPacket(data));
% }
% return list;
%}
%
% Add the TYPE_RELIABLE header to the data
makeReliablePacket(Data, SeqNum) ->
#reliable_packet{ data = <<?TYPE_RELIABLE:?U8,SeqNum:?U16,Data/binary>> }.

View File

@ -0,0 +1,20 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(connection_channel).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-compile(export_all).
-define(RPB, connection_reliable_packet_buffer).
-define(ISB, connection_incoming_split_buffer).
init(ChannelID) ->
ets:new(channels,[public, named_table]),
?RPB:init(list_to_atom(atom_to_list(incoming_reliables_) ++ integer_to_list(ChannelID))),
?RPB:init(list_to_atom(atom_to_list(outgoing_reliables_) ++ integer_to_list(ChannelID))),
?ISB:init(list_to_atom(atom_to_list(incoming_splits_) ++ integer_to_list(ChannelID))).

View File

@ -0,0 +1,129 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(connection_incoming_split_buffer).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-export([insert/2, removeUnreliableTimedOuts/2]).
%-define(TABLE_ID, ?MODULE).
init(Table) ->
ets:new(Table, [public, named_table]),
ok.
insert(Table, BufferedPacket, Reliable) ->
HeaderSize = ?BASE_HEADER_SIZE+7,
<<_Header:?BASE_HEADER_SIZE, Type:?U8, SeqNum:?U16, ChunkCount:?U16, ChunkNum:?U16, _Other/binary>> = BufferedPacket,
case ets:lookup(Table, SeqNum) ->
[{SeqNum, FindPacket}] -> io:format("IncomingSplitPacket with SeqNum=~p is found in ReliablePacketBuffer~n",[SeqNum]);
[] -> ets:insert(Table, {SeqNum, BufferedPacket}).
%
% This will throw a GotSplitPacketException when a full
% split packet is constructed.
%
% SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
%{
% u32 headersize = BASE_HEADER_SIZE + 7;
% assert(p.data.getSize() >= headersize);
% u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]);
% assert(type == TYPE_SPLIT);
% u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]);
% u16 chunk_count = readU16(&p.data[BASE_HEADER_SIZE+3]);
% u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]);
%
% // Add if doesn't exist
% if(m_buf.find(seqnum) == NULL)
% {
% IncomingSplitPacket *sp = new IncomingSplitPacket();
% sp->chunk_count = chunk_count;
% sp->reliable = reliable;
% m_buf[seqnum] = sp;
% }
%
% IncomingSplitPacket *sp = m_buf[seqnum];
%
% // TODO: These errors should be thrown or something? Dunno.
% if(chunk_count != sp->chunk_count)
% derr_con<<"Connection: WARNING: chunk_count="<<chunk_count
% <<" != sp->chunk_count="<<sp->chunk_count
% <<std::endl;
% if(reliable != sp->reliable)
% derr_con<<"Connection: WARNING: reliable="<<reliable
% <<" != sp->reliable="<<sp->reliable
% <<std::endl;
%
% // If chunk already exists, cancel
% if(sp->chunks.find(chunk_num) != NULL)
% throw AlreadyExistsException("Chunk already in buffer");
%
% // Cut chunk data out of packet
% u32 chunkdatasize = p.data.getSize() - headersize;
% SharedBuffer<u8> chunkdata(chunkdatasize);
% memcpy(*chunkdata, &(p.data[headersize]), chunkdatasize);
%
% // Set chunk data in buffer
% sp->chunks[chunk_num] = chunkdata;
%
% // If not all chunks are received, return empty buffer
% if(sp->allReceived() == false)
% return SharedBuffer<u8>();
%
% // Calculate total size
% u32 totalsize = 0;
% core::map<u16, SharedBuffer<u8> >::Iterator i;
% i = sp->chunks.getIterator();
% for(; i.atEnd() == false; i++)
% {
% totalsize += i.getNode()->getValue().getSize();
% }
%
% SharedBuffer<u8> fulldata(totalsize);
%
% // Copy chunks to data buffer
% u32 start = 0;
% for(u32 chunk_i=0; chunk_i<sp->chunk_count;
% chunk_i++)
% {
% SharedBuffer<u8> buf = sp->chunks[chunk_i];
% u16 chunkdatasize = buf.getSize();
% memcpy(&fulldata[start], *buf, chunkdatasize);
% start += chunkdatasize;;
% }
%
% // Remove sp from buffer
% m_buf.remove(seqnum);
% delete sp;
%
% return fulldata;
% }
removeUnreliableTimedOuts(Table) -> ok.
% void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout)
% {
% core::list<u16> remove_queue;
% core::map<u16, IncomingSplitPacket*>::Iterator i;
% i = m_buf.getIterator();
% for(; i.atEnd() == false; i++)
% {
% IncomingSplitPacket *p = i.getNode()->getValue();
% // Reliable ones are not removed by timeout
% if(p->reliable == true)
% continue;
% p->time += dtime;
% if(p->time >= timeout)
% remove_queue.push_back(i.getNode()->getKey());
% }
% core::list<u16>::Iterator j;
% j = remove_queue.begin();
% for(; j != remove_queue.end(); j++)
% {
% dout_con<<"NOTE: Removing timed out unreliable split packet"
% <<std::endl;
% delete m_buf[*j];
% m_buf.remove(*j);
% }
% }

View File

@ -0,0 +1,148 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(connection_reliable_packet_buffer).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-include("connection.hrl").
-compile(export_all).
%-define(TABLE_ID, ?MODULE).
init(Table) ->
ets:new(Table, [public, named_table]).
insert(Table, #buffered_packet{data=BufferedPacket, time=_Time, totaltime=_TotalTime, address = _Address} = BPrec) ->
<<_Header:?BASE_HEADER_SIZE/binary, Type:?U8, SeqNum:?U16,_Other/binary>> = BufferedPacket,
io:format("Type=~p, SeqNum=~p~n",[Type,SeqNum]),
case ets:lookup(Table, SeqNum) of
[{SeqNum, FindPacket}] ->
io:format("Reliable packet with SeqNum=~p is found in ReliablePacketBuffer~n",[SeqNum]),
{exists,FindPacket};
[] -> ets:insert(Table, {SeqNum, BPrec}),
{ok,BPrec}
end.
test_insert(Table) ->
insert(Table, #buffered_packet{data=list_to_binary([1,2,3,4,5,6,7,33,00,34,77,77,77,77]), time=0, totaltime=0}),
insert(Table, #buffered_packet{data=list_to_binary([1,2,3,4,5,6,7,33,01,11,77,77,77,77]), time=0, totaltime=0}),
insert(Table, #buffered_packet{data=list_to_binary([1,2,3,4,5,6,7,33,02,62,77,77,77,77]), time=0, totaltime=0}),
insert(Table, #buffered_packet{data=list_to_binary([1,2,3,4,5,6,7,33,67,02,77,77,77,77]), time=0, totaltime=0}),
insert(Table, #buffered_packet{data=list_to_binary([1,2,3,4,5,6,7,33,00,10,77,77,77,77]), time=0, totaltime=0}).
findPacket(Table, SeqNum) ->
case ets:lookup(Table, SeqNum) of
[{SeqNum, FindPacket}] -> {ok, FindPacket};
[]-> {error, not_found}
end.
test_findPacket(Table)->
findPacket(Table, 15874). % 8704, 2817, 579
print(Table) ->
ets:foldl(fun({SeqNum, Packet},AccIn) ->
io:format("SeqNum=~p: ~p ~n", [SeqNum,Packet]),
AccIn
end, end_list, Table).
empty(Table) ->
Size = ets:info(Table, size),
case Size of
0 -> true;
_ -> Size
end.
size(Table) ->
ets:info(Table, size).
% TODO Check the returning value
notFound(Table) ->
ets:last(Table).
%RPBSearchResult ReliablePacketBuffer::notFound()
%{
% return m_list.end();
%}
getFirstSeqnum(Table) ->
ets:first(Table).
popFirst(Table) ->
FirstSeqNum = ets:first(Table),
#buffered_packet{data=FirstPacket} = ets:lookup(Table, FirstSeqNum),
<<_Header:?BASE_HEADER_SIZE,_Type:?U8,SeqNum:?U16,_Other/binary>> = FirstPacket,
ets:delete(Table, SeqNum),
#buffered_packet{data=FirstPacket}.
popSeqnum(Table, SeqNum) ->
FindPacket = ets:lookup(Table, SeqNum),
ets:delete(Table, SeqNum),
FindPacket.
incrementTimeouts(Table, Dtime) ->
ets:foldl(fun({SeqNum, Packet},AccIn) ->
#buffered_packet{data = Data, time = Time, totaltime = TotalTime, address = Address} = Packet,
ets:update_element(Table, SeqNum,
{2,#buffered_packet{data=Data,time = Time + Dtime, totaltime = TotalTime + Dtime, address = Address}}),
AccIn
end, end_list, Table).
test_increment()->
init(rpb_test),
test_insert(rpb_test),
print(rpb_test),
incrementTimeouts(rpb_test, 35),
print(rpb_test).
resetTimedOuts(Table, Timeout) ->
ets:foldl(fun({SeqNum, Packet},AccIn) ->
#buffered_packet{data = Data, time = Time, totaltime = TotalTime, address = Address} = Packet,
if Time >= Timeout ->
ets:update_element(Table, SeqNum,
{2,#buffered_packet{data=Data,time = 0, totaltime = TotalTime, address = Address}});
true -> ok
end,
AccIn
end, end_list, Table).
anyTotaltimeReached(Table, Timeout) ->
%ets:select(Table, ets:fun2ms(fun({SeqNum,#buffered_packet{totaltime = TotalTime}}) when TotalTime >= Timeout -> true end)).
%ets:select(Table,
% ets:fun2ms(fun({Seq,#buffered_packet{data = _, time = _, totaltime = TotalTime, address = _}}) -> TotalTime end)).
%TODO This construction need to refactoring
case ets:select(Table, [{{'_',#buffered_packet{data='_',time='_',totaltime='$1',address='_'}},[{'>=','$1',Timeout}],['$1']}]) of
[] -> false;
_ -> true % or we must check list size
end.
%bool ReliablePacketBuffer::anyTotaltimeReached(float timeout)
%{
% core::list<BufferedPacket>::Iterator i;
% i = m_list.begin();
% for(; i != m_list.end(); i++){
% if(i->totaltime >= timeout)
% return true;
% }
% return false;
%}
getTimedOuts(Table, Timeout) ->
% TODO - Check the returning value: list of buffered_packet, or SeqNum
ets:select(Table, [{{'$1',#buffered_packet{data='_',time='$2',totaltime='_',address='_'}},[{'>=','$2',Timeout}],['$1']}]).
% core::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout)
%{
% core::list<BufferedPacket> timed_outs;
% core::list<BufferedPacket>::Iterator i;
% i = m_list.begin();
% for(; i != m_list.end(); i++)
% {
% if(i->time >= timeout)
% timed_outs.push_back(*i);
% }
% return timed_outs;
%}

50
src/erlmines.erl Normal file
View File

@ -0,0 +1,50 @@
-module(erlmines).
%%-behaviour(gen_server).
-include("erlmines.hrl").
-export([server/1]).
server(Port) ->
{ok,Socket} = gen_udp:open(Port,[binary]),
listen(Socket).
listen(Socket) ->
receive
%%{udp,Socket,Host,Port,Bin} = Message ->
{udp,Socket,_,_,Bin} ->
io:format("========================================~n",[]),
io:format("Data= ~p~n",[Bin]),
process_packet(Bin),
listen(Socket)
after 10000 ->
gen_udp:close(Socket),
{exit,timeout}
end.
process_packet(<<ProtocolId:?U32, SenderPeerId:?U16, Channel:?U8, PacketType:?U8, Data/binary>>) ->
case PacketType of
?TYPE_CONTROL -> Type = "TYPE_CONTROL (0)",
type_control(Data);
?TYPE_ORIGINAL -> Type = "TYPE_ORIGINAL(1)",
type_original(Data);
?TYPE_SPLIT -> Type = "TYPE_SPLIT(2)",
type_split(Data);
?TYPE_RELIABLE -> Type = "TYPE_RELIABLE(2)",
type_reliable(Data)
end,
io:format("ProtocolId: ~p, SenderPeerId:~p, Channel:~p, PacketType: ~p, Data:~p~n",[ProtocolId,SenderPeerId,Channel,Type,Data]).
type_control(<<ControlType:?U8,Data/binary>>)-> ok.
type_original(Data)-> ok.
type_split(<<SeqNum:?U16,ChunkCount:?U16,ChunkNum:?U16,Data/binary>>)-> ok.
type_reliable(<<SeqNum:?U16,Data/binary>>)-> ok.

14
src/erlmines_app.erl Normal file
View File

@ -0,0 +1,14 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(erlmines_app).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, StartArgs) ->
erlmines_sup:start_link(StartArgs).
stop(_State) ->
ok.

31
src/erlmines_sup.erl Normal file
View File

@ -0,0 +1,31 @@
%%
%% erlmines - The minetest server written in Erlang
%% Copyright (C) 2012 hummermania, Markevych Alexander <rabota.pmr@gmail.com>
%%
-module(erlmines_sup).
-author('Alexander Markevych <rabota.pmr@gmail.com>').
-behaviour(supervisor).
-export([start/0, start_link/1, init/1]).
start() ->
spawn(fun() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = [])
end).
start_link(Args) ->
supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
init([]) ->
case application:get_env(pidfile) of
{ok, Location} ->
Pid = os:getpid(),
ok = file:write_file(Location, list_to_binary(Pid));
undefined -> ok
end,
{ok, {{one_for_one, 100, 300},
[{erlmines,
{erlmines, start_link, []},
permanent, 10000, worker, [erlmines]}
]}}.