aboutsummaryrefslogtreecommitdiff
path: root/src/genericobject.cpp
blob: 368cae1ff8b18d56264e69ca4b42c045e9812b6b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser 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.
*/

#include "genericobject.h"
#include <sstream>
#include "util/serialize.h"

std::string gob_cmd_set_properties(const ObjectProperties &prop)
{
	std::ostringstream os(std::ios::binary);
	writeU8(os, GENERIC_CMD_SET_PROPERTIES);
	prop.serialize(os);
	return os.str();
}

ObjectProperties gob_read_set_properties(std::istream &is)
{
	ObjectProperties prop;
	prop.deSerialize(is);
	return prop;
}

std::string gob_cmd_update_position(
	v3f position,
	v3f velocity,
	v3f acceleration,
	f32 yaw,
	bool do_interpolate,
	bool is_movement_end,
	f32 update_interval
){
	std::ostringstream os(std::ios::binary);
	// command
	writeU8(os, GENERIC_CMD_UPDATE_POSITION);
	// pos
	writeV3F1000(os, position);
	// velocity
	writeV3F1000(os, velocity);
	// acceleration
	writeV3F1000(os, acceleration);
	// yaw
	writeF1000(os, yaw);
	// do_interpolate
	writeU8(os, do_interpolate);
	// is_end_position (for interpolation)
	writeU8(os, is_movement_end);
	// update_interval (for interpolation)
	writeF1000(os, update_interval);
	return os.str();
}

std::string gob_cmd_set_texture_mod(const std::string &mod)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_SET_TEXTURE_MOD);
	// parameters
	os<<serializeString(mod);
	return os.str();
}

std::string gob_cmd_set_sprite(
	v2s16 p,
	u16 num_frames,
	f32 framelength,
	bool select_horiz_by_yawpitch
){
	std::ostringstream os(std::ios::binary);
	// command
	writeU8(os, GENERIC_CMD_SET_SPRITE);
	// parameters
	writeV2S16(os, p);
	writeU16(os, num_frames);
	writeF1000(os, framelength);
	writeU8(os, select_horiz_by_yawpitch);
	return os.str();
}

std::string gob_cmd_punched(s16 damage, s16 result_hp)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_PUNCHED);
	// damage
	writeS16(os, damage);
	// result_hp
	writeS16(os, result_hp);
	return os.str();
}

std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups)
{
	std::ostringstream os(std::ios::binary);
	writeU8(os, GENERIC_CMD_UPDATE_ARMOR_GROUPS);
	writeU16(os, armor_groups.size());
	for(ItemGroupList::const_iterator i = armor_groups.begin();
			i != armor_groups.end(); ++i){
		os<<serializeString(i->first);
		writeS16(os, i->second);
	}
	return os.str();
}

std::string gob_cmd_update_physics_override(float physics_override_speed, float physics_override_jump,
		float physics_override_gravity, bool sneak, bool sneak_glitch)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_SET_PHYSICS_OVERRIDE);
	// parameters
	writeF1000(os, physics_override_speed);
	writeF1000(os, physics_override_jump);
	writeF1000(os, physics_override_gravity);
	// these are sent inverted so we get true when the server sends nothing
	writeU8(os, !sneak);
	writeU8(os, !sneak_glitch);
	return os.str();
}

std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_SET_ANIMATION);
	// parameters
	writeV2F1000(os, frames);
	writeF1000(os, frame_speed);
	writeF1000(os, frame_blend);
	// these are sent inverted so we get true when the server sends nothing
	writeU8(os, !frame_loop);
	return os.str();
}

std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_SET_BONE_POSITION);
	// parameters
	os<<serializeString(bone);
	writeV3F1000(os, position);
	writeV3F1000(os, rotation);
	return os.str();
}

std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation)
{
	std::ostringstream os(std::ios::binary);
	// command 
	writeU8(os, GENERIC_CMD_ATTACH_TO);
	// parameters
	writeS16(os, parent_id);
	os<<serializeString(bone);
	writeV3F1000(os, position);
	writeV3F1000(os, rotation);
	return os.str();
}

std::string gob_cmd_update_nametag_attributes(video::SColor color)
{
	std::ostringstream os(std::ios::binary);
	// command
	writeU8(os, GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES);
	// parameters
	writeU8(os, 1); // version for forward compatibility
	writeARGB8(os, color);
	return os.str();
}
BaseException { public: ThrottlingException(const char *s): BaseException(s) {} };*/ class InvalidIncomingDataException : public BaseException { public: InvalidIncomingDataException(const char *s): BaseException(s) {} }; class InvalidOutgoingDataException : public BaseException { public: InvalidOutgoingDataException(const char *s): BaseException(s) {} }; class NoIncomingDataException : public BaseException { public: NoIncomingDataException(const char *s): BaseException(s) {} }; class ProcessedSilentlyException : public BaseException { public: ProcessedSilentlyException(const char *s): BaseException(s) {} }; #define SEQNUM_MAX 65535 inline bool seqnum_higher(u16 higher, u16 lower) { if(lower > higher && lower - higher > SEQNUM_MAX/2){ return true; } return (higher > lower); } struct BufferedPacket { BufferedPacket(u8 *a_data, u32 a_size): data(a_data, a_size), time(0.0), totaltime(0.0) {} BufferedPacket(u32 a_size): data(a_size), time(0.0), totaltime(0.0) {} SharedBuffer<u8> data; // Data of the packet, including headers float time; // Seconds from buffering the packet or re-sending float totaltime; // Seconds from buffering the packet Address address; // Sender or destination }; // This adds the base headers to the data and makes a packet out of it BufferedPacket makePacket(Address &address, u8 *data, u32 datasize, u32 protocol_id, u16 sender_peer_id, u8 channel); BufferedPacket makePacket(Address &address, SharedBuffer<u8> &data, u32 protocol_id, u16 sender_peer_id, u8 channel); // Add the TYPE_ORIGINAL header to the data SharedBuffer<u8> makeOriginalPacket( SharedBuffer<u8> data); // Split data in chunks and add TYPE_SPLIT headers to them std::list<SharedBuffer<u8> > makeSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 seqnum); // Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet // Increments split_seqnum if a split packet is made std::list<SharedBuffer<u8> > makeAutoSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 &split_seqnum); // Add the TYPE_RELIABLE header to the data SharedBuffer<u8> makeReliablePacket( SharedBuffer<u8> data, u16 seqnum); struct IncomingSplitPacket { IncomingSplitPacket() { time = 0.0; reliable = false; } // Key is chunk number, value is data without headers std::map<u16, SharedBuffer<u8> > chunks; u32 chunk_count; float time; // Seconds from adding bool reliable; // If true, isn't deleted on timeout bool allReceived() { return (chunks.size() == chunk_count); } }; /* === 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 (PEER_ID_INEXISTENT) is reserved for making new connections value 1 (PEER_ID_SERVER) is reserved for server these constants are defined in constants.h channel: The lower the number, the higher the priority is. Only channels 0, 1 and 2 exist. */ #define BASE_HEADER_SIZE 7 #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 /* A buffer which stores reliable packets and sorts them internally for fast access to the smallest one. */ typedef std::list<BufferedPacket>::iterator RPBSearchResult; class ReliablePacketBuffer { public: ReliablePacketBuffer(); void print(); bool empty(); u32 size(); RPBSearchResult findPacket(u16 seqnum); RPBSearchResult notFound(); bool getFirstSeqnum(u16 *result); BufferedPacket popFirst(); BufferedPacket popSeqnum(u16 seqnum); void insert(BufferedPacket &p); void incrementTimeouts(float dtime); void resetTimedOuts(float timeout); bool anyTotaltimeReached(float timeout); std::list<BufferedPacket> getTimedOuts(float timeout); private: std::list<BufferedPacket> m_list; u16 m_list_size; }; /* A buffer for reconstructing split packets */ class IncomingSplitBuffer { public: ~IncomingSplitBuffer(); /* Returns a reference counted buffer of length != 0 when a full split packet is constructed. If not, returns one of length 0. */ SharedBuffer<u8> insert(BufferedPacket &p, bool reliable); void removeUnreliableTimedOuts(float dtime, float timeout); private: // Key is seqnum std::map<u16, IncomingSplitPacket*> m_buf; }; class Connection; struct Channel { Channel(); ~Channel(); u16 next_outgoing_seqnum; u16 next_incoming_seqnum; u16 next_outgoing_split_seqnum; // This is for buffering the incoming packets that are coming in // the wrong order ReliablePacketBuffer incoming_reliables; // This is for buffering the sent packets so that the sender can // re-send them if no ACK is received ReliablePacketBuffer outgoing_reliables; IncomingSplitBuffer incoming_splits; }; class Peer; class PeerHandler { public: PeerHandler() { } virtual ~PeerHandler() { } /* This is called after the Peer has been inserted into the Connection's peer container. */ virtual void peerAdded(Peer *peer) = 0; /* This is called before the Peer has been removed from the Connection's peer container. */ virtual void deletingPeer(Peer *peer, bool timeout) = 0; }; class Peer { public: Peer(u16 a_id, Address a_address); virtual ~Peer(); /* Calculates avg_rtt and resend_timeout. rtt=-1 only recalculates resend_timeout */ void reportRTT(float rtt); Channel channels[CHANNEL_COUNT]; // Address of the peer Address address; // Unique id of the peer u16 id; // Seconds from last receive float timeout_counter; // Ping timer float ping_timer; // This is changed dynamically float resend_timeout; // Updated when an ACK is received float avg_rtt; // This is set to true when the peer has actually sent something // with the id we have given to it bool has_sent_with_id; float m_sendtime_accu; float m_max_packets_per_second; int m_num_sent; int m_max_num_sent; // Updated from configuration by Connection float congestion_control_aim_rtt; float congestion_control_max_rate; float congestion_control_min_rate; private: }; /* Connection */ struct OutgoingPacket { u16 peer_id; u8 channelnum; SharedBuffer<u8> data; bool reliable; OutgoingPacket(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_, bool reliable_): peer_id(peer_id_), channelnum(channelnum_), data(data_), reliable(reliable_) { } }; enum ConnectionEventType{ CONNEVENT_NONE, CONNEVENT_DATA_RECEIVED, CONNEVENT_PEER_ADDED, CONNEVENT_PEER_REMOVED, CONNEVENT_BIND_FAILED, }; struct ConnectionEvent { enum ConnectionEventType type; u16 peer_id; Buffer<u8> data; bool timeout; Address address; ConnectionEvent(): type(CONNEVENT_NONE) {} std::string describe() { switch(type){ case CONNEVENT_NONE: return "CONNEVENT_NONE"; case CONNEVENT_DATA_RECEIVED: return "CONNEVENT_DATA_RECEIVED"; case CONNEVENT_PEER_ADDED: return "CONNEVENT_PEER_ADDED"; case CONNEVENT_PEER_REMOVED: return "CONNEVENT_PEER_REMOVED"; case CONNEVENT_BIND_FAILED: return "CONNEVENT_BIND_FAILED"; } return "Invalid ConnectionEvent"; } void dataReceived(u16 peer_id_, SharedBuffer<u8> data_) { type = CONNEVENT_DATA_RECEIVED; peer_id = peer_id_; data = data_; } void peerAdded(u16 peer_id_, Address address_) { type = CONNEVENT_PEER_ADDED; peer_id = peer_id_; address = address_; } void peerRemoved(u16 peer_id_, bool timeout_, Address address_) { type = CONNEVENT_PEER_REMOVED; peer_id = peer_id_; timeout = timeout_; address = address_; } void bindFailed() { type = CONNEVENT_BIND_FAILED; } }; enum ConnectionCommandType{ CONNCMD_NONE, CONNCMD_SERVE, CONNCMD_CONNECT, CONNCMD_DISCONNECT, CONNCMD_SEND, CONNCMD_SEND_TO_ALL, CONNCMD_DELETE_PEER, }; struct ConnectionCommand { enum ConnectionCommandType type; u16 port; Address address; u16 peer_id; u8 channelnum; Buffer<u8> data; bool reliable; ConnectionCommand(): type(CONNCMD_NONE) {} void serve(u16 port_) { type = CONNCMD_SERVE; port = port_; } void connect(Address address_) { type = CONNCMD_CONNECT; address = address_; } void disconnect() { type = CONNCMD_DISCONNECT; } void send(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_, bool reliable_) { type = CONNCMD_SEND; peer_id = peer_id_; channelnum = channelnum_; data = data_; reliable = reliable_; } void sendToAll(u8 channelnum_, SharedBuffer<u8> data_, bool reliable_) { type = CONNCMD_SEND_TO_ALL; channelnum = channelnum_; data = data_; reliable = reliable_; } void deletePeer(u16 peer_id_) { type = CONNCMD_DELETE_PEER; peer_id = peer_id_; } }; class Connection: public SimpleThread { public: Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6); Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6, PeerHandler *peerhandler); ~Connection(); void * Thread(); /* Interface */ ConnectionEvent getEvent(); ConnectionEvent waitEvent(u32 timeout_ms); void putCommand(ConnectionCommand &c); void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; } void Serve(unsigned short port); void Connect(Address address); bool Connected(); void Disconnect(); u32 Receive(u16 &peer_id, SharedBuffer<u8> &data); void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable); void Send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); void RunTimeouts(float dtime); // dummy u16 GetPeerID(){ return m_peer_id; } Address GetPeerAddress(u16 peer_id); float GetPeerAvgRTT(u16 peer_id); void DeletePeer(u16 peer_id); private: void putEvent(ConnectionEvent &e); void processCommand(ConnectionCommand &c); void send(float dtime); void receive(); void runTimeouts(float dtime); void serve(u16 port); void connect(Address address); void disconnect(); void sendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable); void send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); void sendAsPacket(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); void rawSendAsPacket(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); void rawSend(const BufferedPacket &packet); Peer* getPeer(u16 peer_id); Peer* getPeerNoEx(u16 peer_id); std::list<Peer*> getPeers(); bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst); // Returns next data from a buffer if possible // If found, returns true; if not, false. // If found, sets peer_id and dst bool checkIncomingBuffers(Channel *channel, u16 &peer_id, SharedBuffer<u8> &dst); /* Processes a packet with the basic header stripped out. Parameters: packetdata: Data in packet (with no base headers) peer_id: peer id of the sender of the packet in question channelnum: channel on which the packet was sent reliable: true if recursing into a reliable packet */ SharedBuffer<u8> processPacket(Channel *channel, SharedBuffer<u8> packetdata, u16 peer_id, u8 channelnum, bool reliable); bool deletePeer(u16 peer_id, bool timeout); Queue<OutgoingPacket> m_outgoing_queue; MutexedQueue<ConnectionEvent> m_event_queue; MutexedQueue<ConnectionCommand> m_command_queue; u32 m_protocol_id; u32 m_max_packet_size; float m_timeout; UDPSocket m_socket; u16 m_peer_id; std::map<u16, Peer*> m_peers; JMutex m_peers_mutex; // Backwards compatibility PeerHandler *m_bc_peerhandler; int m_bc_receive_timeout; void SetPeerID(u16 id){ m_peer_id = id; } u32 GetProtocolID(){ return m_protocol_id; } void PrintInfo(std::ostream &out); void PrintInfo(); std::string getDesc(); u16 m_indentation; }; } // namespace #endif