Skip to content
Permalink
Browse files

Implement mod communication channels (#6351)

Implement network communication for channels

* Implement ModChannel manager server side to route incoming messages from clients to other clients
* Add signal handler switch on client & ModChannelMgr on client to handle channels
* Add Lua API bindings + client packet sending + unittests
* Implement server message sending
* Add callback from received message handler to Lua API using registration method
  • Loading branch information...
nerzhul committed Sep 25, 2017
1 parent 6df312a commit 6f1c90720402415b62fb4d5e809ec7dbc1cd7f96
@@ -71,3 +71,5 @@ core.registered_on_dignode, core.register_on_dignode = make_registration()
core.registered_on_punchnode, core.register_on_punchnode = make_registration()
core.registered_on_placenode, core.register_on_placenode = make_registration()
core.registered_on_item_use, core.register_on_item_use = make_registration()
core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration()
core.registered_on_modchannel_signal, core.register_on_modchannel_signal = make_registration()
@@ -583,6 +583,7 @@ core.registered_on_punchplayers, core.register_on_punchplayer = make_registratio
core.registered_on_priv_grant, core.register_on_priv_grant = make_registration()
core.registered_on_priv_revoke, core.register_on_priv_revoke = make_registration()
core.registered_can_bypass_userlimit, core.register_can_bypass_userlimit = make_registration()
core.registered_on_modchannel_message, core.register_on_modchannel_message = make_registration()

--
-- Compatibility for on_mapgen_init()
@@ -928,6 +928,9 @@ player_transfer_distance (Player transfer distance) int 0
# Whether to allow players to damage and kill each other.
enable_pvp (Player versus Player) bool true

# Enable mod channels support.
enable_mod_channels (Mod channels) bool false

# If this is set, players will always (re)spawn at the given position.
static_spawnpoint (Static spawnpoint) string

@@ -1,5 +1,6 @@
local modname = core.get_current_modname() or "??"
local modstorage = core.get_mod_storage()
local mod_channel

dofile("preview:example.lua")
-- This is an example function to ensure it's working properly, should be removed before merge
@@ -14,6 +15,21 @@ core.register_on_connect(function()
print("Server ip: " .. server_info.ip)
print("Server address: " .. server_info.address)
print("Server port: " .. server_info.port)

mod_channel = core.mod_channel_join("experimental_preview")
end)

core.register_on_modchannel_message(function(channel, sender, message)
print("[PREVIEW][modchannels] Received message `" .. message .. "` on channel `"
.. channel .. "` from sender `" .. sender .. "`")
core.after(1, function()
mod_channel:send_all("CSM preview received " .. message)
end)
end)

core.register_on_modchannel_signal(function(channel, signal)
print("[PREVIEW][modchannels] Received signal id `" .. signal .. "` on channel `"
.. channel)
end)

core.register_on_placenode(function(pointed_thing, node)
@@ -100,6 +116,12 @@ core.after(2, function()
preview_minimap()
end)

core.after(4, function()
if mod_channel:is_writeable() then
mod_channel:send_all("preview talk to experimental")
end
end)

core.after(5, function()
if core.ui.minimap then
core.ui.minimap:show()
@@ -683,6 +683,12 @@ Call these functions only at load time!
* Called when the local player uses an item.
* Newest functions are called first.
* If any function returns true, the item use is not sent to server.
* `minetest.register_on_modchannel_message(func(channel_name, sender, message))`
* Called when an incoming mod channel message is received
* You must have joined some channels before, and server must acknowledge the
join request.
* If message comes from a server mod, `sender` field is an empty string.

### Sounds
* `minetest.sound_play(spec, parameters)`: returns a handle
* `spec` is a `SimpleSoundSpec`
@@ -754,6 +760,16 @@ Call these functions only at load time!
* returns reference to mod private `StorageRef`
* must be called during mod load time

### Mod channels
![Mod channels communication scheme](docs/mod channels.png)

* `minetest.mod_channel_join(channel_name)`
* Client joins channel `channel_name`, and creates it, if necessary. You
should listen from incoming messages with `minetest.register_on_modchannel_message`
call to receive incoming messages. Warning, this function is asynchronous.
* You should use a minetest.register_on_connect(function() ... end) to perform
a successful channel join on client startup.

### Misc.
* `minetest.parse_json(string[, nullvalue])`: returns something
* Convert a string containing JSON data into the Lua equivalent
@@ -827,9 +843,25 @@ Call these functions only at load time!
Class reference
---------------

### ModChannel

An interface to use mod channels on client and server

#### Methods
* `leave()`: leave the mod channel.
* Client leaves channel `channel_name`.
* No more incoming or outgoing messages can be sent to this channel from client mods.
* This invalidate all future object usage
* Ensure your set mod_channel to nil after that to free Lua resources
* `is_writeable()`: returns true if channel is writeable and mod can send over it.
* `send_all(message)`: Send `message` though the mod channel.
* If mod channel is not writeable or invalid, message will be dropped.
* Message size is limited to 65535 characters by protocol.

### Minimap
An interface to manipulate minimap on client UI

#### Methods
* `show()`: shows the minimap (if not disabled by server)
* `hide()`: hides the minimap
* `set_pos(pos)`: sets the minimap position on screen
@@ -1126,7 +1126,7 @@ The 2D perlin noise described by `noise_params` varies the Y co-ordinate of the
stratum midpoint. The 2D perlin noise described by `np_stratum_thickness`
varies the stratum's vertical thickness (in units of nodes). Due to being
continuous across mapchunk borders the stratum's vertical thickness is
unlimited.
unlimited.
`y_min` and `y_max` define the limits of the ore generation and for performance
reasons should be set as close together as possible but without clipping the
stratum's Y variation.
@@ -2496,6 +2496,20 @@ Call these functions only at load time!
* `minetest.register_can_bypass_userlimit(function(name, ip))`
* Called when `name` user connects with `ip`.
* Return `true` to by pass the player limit
* `minetest.register_on_modchannel_message(func(channel_name, sender, message))`
* Called when an incoming mod channel message is received
* You should have joined some channels to receive events.
* If message comes from a server mod, `sender` field is an empty string.
* `minetest.register_on_modchannel_signal(func(channel_name, signal))`
* Called when a valid incoming mod channel signal is received
* Signal id permit to react to server mod channel events
* Possible values are:
0: join_ok
1: join_failed
2: leave_ok
3: leave_failed
4: event_on_not_joined_channel
5: state_changed

### Other registration functions
* `minetest.register_chatcommand(cmd, chatcommand definition)`
@@ -2773,6 +2787,14 @@ and `minetest.auth_reload` call the authetification handler.
* spread these updates to neighbours and can cause a cascade
of nodes to fall.

### Mod channels
You can find mod channels communication scheme in `docs/mod_channels.png`.

* `minetest.mod_channel_join(channel_name)`
* Server joins channel `channel_name`, and creates it if necessary. You
should listen from incoming messages with `minetest.register_on_modchannel_message`
call to receive incoming messages

### Inventory
`minetest.get_inventory(location)`: returns an `InvRef`

@@ -3256,6 +3278,21 @@ These functions return the leftover itemstack.
Class reference
---------------

### ModChannel

An interface to use mod channels on client and server

#### Methods
* `leave()`: leave the mod channel.
* Server leaves channel `channel_name`.
* No more incoming or outgoing messages can be sent to this channel from server mods.
* This invalidate all future object usage
* Ensure your set mod_channel to nil after that to free Lua resources
* `is_writeable()`: returns true if channel is writeable and mod can send over it.
* `send_all(message)`: Send `message` though the mod channel.
* If mod channel is not writeable or invalid, message will be dropped.
* Message size is limited to 65535 characters by protocol.

### `MetaDataRef`
See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`.

BIN +341 KB doc/mod_channels.png
Binary file not shown.
@@ -2,6 +2,8 @@
-- Experimental things
--

dofile(minetest.get_modpath("experimental").."/modchannels.lua")

-- For testing random stuff

experimental = {}
@@ -0,0 +1,16 @@
--
-- Mod channels experimental handlers
--
local mod_channel = core.mod_channel_join("experimental_preview")

core.register_on_modchannel_message(function(channel, sender, message)
print("[minimal][modchannels] Server received message `" .. message
.. "` on channel `" .. channel .. "` from sender `" .. sender .. "`")

if mod_channel:is_writeable() then
mod_channel:send_all("experimental answers to preview")
mod_channel:leave()
end
end)

print("[minimal][modchannels] Code loaded!")
@@ -1126,6 +1126,10 @@
# type: bool
# enable_pvp = true

# Enable mod channels.
# type: bool
# enable_mod_channels = false

# If this is set, players will always (re)spawn at the given position.
# type: string
# static_spawnpoint =
@@ -420,6 +420,7 @@ set(common_SRCS
mg_decoration.cpp
mg_ore.cpp
mg_schematic.cpp
modchannels.cpp
mods.cpp
nameidmapping.cpp
nodedef.cpp
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h"
#include "mapblock.h"
#include "minimap.h"
#include "modchannels.h"
#include "mods.h"
#include "profiler.h"
#include "shader.h"
@@ -94,7 +95,8 @@ Client::Client(
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
m_media_downloader(new ClientMediaDownloader()),
m_state(LC_Created),
m_game_ui_flags(game_ui_flags)
m_game_ui_flags(game_ui_flags),
m_modchannel_mgr(new ModChannelMgr())
{
// Add local player
m_env.setLocalPlayer(new LocalPlayer(this, playername));
@@ -1919,3 +1921,57 @@ std::string Client::getModStoragePath() const
{
return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
}

/*
* Mod channels
*/

bool Client::joinModChannel(const std::string &channel)
{
if (m_modchannel_mgr->channelRegistered(channel))
return false;

NetworkPacket pkt(TOSERVER_MODCHANNEL_JOIN, 2 + channel.size());
pkt << channel;
Send(&pkt);

m_modchannel_mgr->joinChannel(channel, 0);
return true;
}

bool Client::leaveModChannel(const std::string &channel)
{
if (!m_modchannel_mgr->channelRegistered(channel))
return false;

NetworkPacket pkt(TOSERVER_MODCHANNEL_LEAVE, 2 + channel.size());
pkt << channel;
Send(&pkt);

m_modchannel_mgr->leaveChannel(channel, 0);
return true;
}

bool Client::sendModChannelMessage(const std::string &channel, const std::string &message)
{
if (!m_modchannel_mgr->canWriteOnChannel(channel))
return false;

if (message.size() > STRING_MAX_LEN) {
warningstream << "ModChannel message too long, dropping before sending "
<< " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: "
<< channel << ")" << std::endl;
return false;
}

// @TODO: do some client rate limiting
NetworkPacket pkt(TOSERVER_MODCHANNEL_MSG, 2 + channel.size() + 2 + message.size());
pkt << channel << message;
Send(&pkt);
return true;
}

ModChannel* Client::getModChannel(const std::string &channel)
{
return m_modchannel_mgr->getModChannel(channel);
}
@@ -52,6 +52,7 @@ class IWritableNodeDefManager;
//class IWritableCraftDefManager;
class ClientMediaDownloader;
struct MapDrawControl;
class ModChannelMgr;
class MtEventManager;
struct PointedThing;
class MapDatabase;
@@ -224,6 +225,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
void handleCommand_EyeOffset(NetworkPacket* pkt);
void handleCommand_UpdatePlayerList(NetworkPacket* pkt);
void handleCommand_ModChannelMsg(NetworkPacket *pkt);
void handleCommand_ModChannelSignal(NetworkPacket *pkt);
void handleCommand_SrpBytesSandB(NetworkPacket* pkt);
void handleCommand_CSMFlavourLimits(NetworkPacket *pkt);

@@ -424,6 +427,11 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
return m_csm_noderange_limit;
}

bool joinModChannel(const std::string &channel);
bool leaveModChannel(const std::string &channel);
bool sendModChannelMessage(const std::string &channel, const std::string &message);
ModChannel *getModChannel(const std::string &channel);

private:

// Virtual methods from con::PeerHandler
@@ -580,4 +588,6 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
// CSM flavour limits byteflag
u64 m_csm_flavour_limits = CSMFlavourLimit::CSM_FL_NONE;
u32 m_csm_noderange_limit = 8;

std::unique_ptr<ModChannelMgr> m_modchannel_mgr;
};
@@ -300,6 +300,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("default_password", "");
settings->setDefault("default_privs", "interact, shout");
settings->setDefault("enable_pvp", "true");
settings->setDefault("enable_mod_channels", "false");
settings->setDefault("disallow_empty_password", "false");
settings->setDefault("disable_anticheat", "false");
settings->setDefault("enable_rollback_recording", "false");
@@ -33,6 +33,7 @@ class MtEventManager;
class IRollbackManager;
class EmergeManager;
class Camera;
class ModChannel;
class ModMetadata;

namespace irr { namespace scene {
@@ -78,4 +79,10 @@ class IGameDef
virtual std::string getModStoragePath() const = 0;
virtual bool registerModStorage(ModMetadata *storage) = 0;
virtual void unregisterModStorage(const std::string &name) = 0;

virtual bool joinModChannel(const std::string &channel) = 0;
virtual bool leaveModChannel(const std::string &channel) = 0;
virtual bool sendModChannelMessage(const std::string &channel,
const std::string &message) = 0;
virtual ModChannel *getModChannel(const std::string &channel) = 0;
};
Oops, something went wrong.

0 comments on commit 6f1c907

Please sign in to comment.
You can’t perform that action at this time.