/* Minetest-c55 Copyright (C) 2010 celeron55, Perttu Ahola 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. */ #include "connection.h" #include "main.h" #include "serialization.h" namespace con { BufferedPacket makePacket(Address &address, u8 *data, u32 datasize, u32 protocol_id, u16 sender_peer_id, u8 channel) { u32 packet_size = datasize + BASE_HEADER_SIZE; BufferedPacket p(packet_size); p.address = address; writeU32(&p.data[0], protocol_id); writeU16(&p.data[4], sender_peer_id); writeU8(&p.data[6], channel); memcpy(&p.data[BASE_HEADER_SIZE], data, datasize); return p; } BufferedPacket makePacket(Address &address, SharedBuffer &data, u32 protocol_id, u16 sender_peer_id, u8 channel) { return makePacket(address, *data, data.getSize(), protocol_id, sender_peer_id, channel); } SharedBuffer makeOriginalPacket( SharedBuffer data) { u32 header_size = 1; u32 packet_size = data.getSize() + header_size; SharedBuffer b(packet_size); writeU8(&b[0], TYPE_ORIGINAL); memcpy(&b[header_size], *data, data.getSize()); return b; } core::list > makeSplitPacket( SharedBuffer data, u32 chunksize_max, u16 seqnum) { // Chunk packets, containing the TYPE_SPLIT header core::list > chunks; u32 chunk_header_size = 7; u32 maximum_data_size = chunksize_max - chunk_header_size; u32 start = 0; u32 end = 0; u32 chunk_num = 0; do{ end = start + maximum_data_size - 1; if(end > data.getSize() - 1) end = data.getSize() - 1; u32 payload_size = end - start + 1; u32 packet_size = chunk_header_size + payload_size; SharedBuffer chunk(packet_size); writeU8(&chunk[0], TYPE_SPLIT); writeU16(&chunk[1], seqnum); // [3] u16 chunk_count is written at next stage writeU16(&chunk[5], chunk_num); memcpy(&chunk[chunk_header_size], &data[start], payload_size); chunks.push_back(chunk); start = end + 1; chunk_num++; } while(end != data.getSize() - 1); u16 chunk_count = chunks.getSize(); core::list >::Iterator i = chunks.begin(); for(; i != chunks.end(); i++) { // Write chunk_count writeU16(&((*i)[3]), chunk_count); } return chunks; } core::list > makeAutoSplitPacket( SharedBuffer data, u32 chunksize_max, u16 &split_seqnum) { u32 original_header_size = 1; core::list > 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; } SharedBuffer makeReliablePacket( SharedBuffer data, u16 seqnum) { /*dstream<<"BEGIN SharedBuffer makeReliablePacket()"< makeReliablePacket()"<::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++) { u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); dout_con<::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++) { u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); /*dout_con<<"findPacket(): finding seqnum="<::Iterator i = m_list.begin(); m_list.erase(i); return p; } BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum) { RPBSearchResult r = findPacket(seqnum); if(r == notFound()){ dout_con<<"Not found"<= BASE_HEADER_SIZE+3); u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]); assert(type == TYPE_RELIABLE); u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); // Find the right place for the packet and insert it there // If list is empty, just add it if(m_list.empty()) { m_list.push_back(p); // Done. return; } // Otherwise find the right place core::list::Iterator i; i = m_list.begin(); // Find the first packet in the list which has a higher seqnum for(; i != m_list.end(); i++){ u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); if(s == seqnum){ throw AlreadyExistsException("Same seqnum in list"); } if(seqnum_higher(s, seqnum)){ break; } } // If we're at the end of the list, add the packet to the // end of the list if(i == m_list.end()) { m_list.push_back(p); // Done. return; } // Insert before i m_list.insert_before(i, p); } void ReliablePacketBuffer::incrementTimeouts(float dtime) { core::list::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++){ i->time += dtime; i->totaltime += dtime; } } void ReliablePacketBuffer::resetTimedOuts(float timeout) { core::list::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++){ if(i->time >= timeout) i->time = 0.0; } } bool ReliablePacketBuffer::anyTotaltimeReached(float timeout) { core::list::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++){ if(i->totaltime >= timeout) return true; } return false; } core::list ReliablePacketBuffer::getTimedOuts(float timeout) { core::list timed_outs; core::list::Iterator i; i = m_list.begin(); for(; i != m_list.end(); i++) { if(i->time >= timeout) timed_outs.push_back(*i); } return timed_outs; } /* IncomingSplitBuffer */ IncomingSplitBuffer::~IncomingSplitBuffer() { core::map::Iterator i; i = m_buf.getIterator(); for(; i.atEnd() == false; i++) { delete i.getNode()->getValue(); } } /* This will throw a GotSplitPacketException when a full split packet is constructed. */ SharedBuffer 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="<chunk_count <reliable) derr_con<<"Connection: WARNING: reliable="<reliable="<reliable <chunks.find(chunk_num) != NULL) throw AlreadyExistsException("Chunk already in buffer"); // Cut chunk data out of packet u32 chunkdatasize = p.data.getSize() - headersize; SharedBuffer 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(); // Calculate total size u32 totalsize = 0; core::map >::Iterator i; i = sp->chunks.getIterator(); for(; i.atEnd() == false; i++) { totalsize += i.getNode()->getValue().getSize(); } SharedBuffer fulldata(totalsize); // Copy chunks to data buffer u32 start = 0; for(u32 chunk_i=0; chunk_ichunk_count; chunk_i++) { SharedBuffer 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; } void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout) { core::list remove_queue; core::map::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::Iterator j; j = remove_queue.begin(); for(; j != remove_queue.end(); j++) { dout_con<<"NOTE: Removing timed out unreliable split packet" < RESEND_TIMEOUT_MAX) timeout = RESEND_TIMEOUT_MAX; resend_timeout = timeout; } /* Connection */ Connection::Connection( u32 protocol_id, u32 max_packet_size, float timeout, PeerHandler *peerhandler ) { assert(peerhandler != NULL); m_protocol_id = protocol_id; m_max_packet_size = max_packet_size; m_timeout = timeout; m_peer_id = PEER_ID_INEXISTENT; //m_waiting_new_peer_id = false; m_indentation = 0; m_peerhandler = peerhandler; } Connection::~Connection() { // Clear peers core::map::Iterator j; j = m_peers.getIterator(); for(; j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); delete peer; } } void Connection::Serve(unsigned short port) { m_socket.Bind(port); m_peer_id = PEER_ID_SERVER; } void Connection::Connect(Address address) { core::map::Node *node = m_peers.find(PEER_ID_SERVER); if(node != NULL){ throw ConnectionException("Already connected to a server"); } Peer *peer = new Peer(PEER_ID_SERVER, address); m_peers.insert(peer->id, peer); m_peerhandler->peerAdded(peer); m_socket.Bind(0); // Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT m_peer_id = PEER_ID_INEXISTENT; SharedBuffer data(0); Send(PEER_ID_SERVER, 0, data, true); //m_waiting_new_peer_id = true; } void Connection::Disconnect() { // Create and send DISCO packet SharedBuffer data(2); writeU8(&data[0], TYPE_CONTROL); writeU8(&data[1], CONTROLTYPE_DISCO); // Send to all core::map::Iterator j; j = m_peers.getIterator(); for(; j.atEnd() == false; j++) { Peer *peer = j.getNode()->getValue(); SendAsPacket(peer->id, 0, data, false); } } bool Connection::Connected() { if(m_peers.size() != 1) return false; core::map::Node *node = m_peers.find(PEER_ID_SERVER); if(node == NULL) return false; if(m_peer_id == PEER_ID_INEXISTENT) return false; return true; } SharedBuffer Channel::ProcessPacket( SharedBuffer packetdata, Connection *con, u16 peer_id, u8 channelnum, bool reliable) { IndentationRaiser iraiser(&(con->m_indentation)); if(packetdata.getSize() < 1) throw InvalidIncomingDataException("packetdata.getSize() < 1"); u8 type = readU8(&packetdata[0]); if(type == TYPE_CONTROL) { if(packetdata.getSize() < 2) throw InvalidIncomingDataException("packetdata.getSize() < 2"); u8 controltype = readU8(&packetdata[1]); if(controltype == CONTROLTYPE_ACK) { if(packetdata.getSize() < 4) throw InvalidIncomingDataException ("packetdata.getSize() < 4 (ACK header size)"); u16 seqnum = readU16(&packetdata[2]); con->PrintInfo(); dout_con<<"Got CONTROLTYPE_ACK: channelnum=" <<((int)channelnum&0xff)<<", peer_id="<PrintInfo(); outgoing_reliables.print(); dout_con<PrintInfo(derr_con); derr_con<<"WARNING: ACKed packet not " "in outgoing queue" < 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 "joystick_controller.h" #include "irrlichttypes_extrabloated.h" #include "keys.h" #include "settings.h" #include "gettime.h" #include "porting.h" #include "util/string.h" #include "util/numeric.h" bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const { u32 buttons = ev.ButtonStates; buttons &= filter_mask; return buttons == compare_mask; } bool JoystickAxisCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const { s16 ax_val = ev.Axis[axis_to_compare]; return (ax_val * direction < -thresh); } // spares many characters #define JLO_B_PB(A, B, C) jlo.button_keys.emplace_back(A, B, C) #define JLO_A_PB(A, B, C, D) jlo.axis_keys.emplace_back(A, B, C, D) JoystickLayout create_default_layout() { JoystickLayout jlo; jlo.axes_deadzone = g_settings->getU16("joystick_deadzone"); const JoystickAxisLayout axes[JA_COUNT] = { {0, 1}, // JA_SIDEWARD_MOVE {1, 1}, // JA_FORWARD_MOVE {3, 1}, // JA_FRUSTUM_HORIZONTAL {4, 1}, // JA_FRUSTUM_VERTICAL }; memcpy(jlo.axes, axes, sizeof(jlo.axes)); u32 sb = 1 << 7; // START button mask u32 fb = 1 << 3; // FOUR button mask u32 bm = sb | fb; // Mask for Both Modifiers // The back button means "ESC". JLO_B_PB(KeyType::ESC, 1 << 6, 1 << 6); // The start button counts as modifier as well as use key. // JLO_B_PB(KeyType::USE, sb, sb)); // Accessible without start modifier button pressed // regardless whether four is pressed or not JLO_B_PB(KeyType::SNEAK, sb | 1 << 2, 1 << 2); // Accessible without four modifier button pressed // regardless whether start is pressed or not JLO_B_PB(KeyType::DIG, fb | 1 << 4, 1 << 4); JLO_B_PB(KeyType::PLACE, fb | 1 << 5, 1 << 5); // Accessible without any modifier pressed JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); JLO_B_PB(KeyType::AUX1, bm | 1 << 1, 1 << 1); // Accessible with start button not pressed, but four pressed // TODO find usage for button 0 JLO_B_PB(KeyType::DROP, bm | 1 << 1, fb | 1 << 1); JLO_B_PB(KeyType::HOTBAR_PREV, bm | 1 << 4, fb | 1 << 4); JLO_B_PB(KeyType::HOTBAR_NEXT, bm | 1 << 5, fb | 1 << 5); // Accessible with start button and four pressed // TODO find usage for buttons 0, 1 and 4, 5 // Now about the buttons simulated by the axes // Movement buttons, important for vessels JLO_A_PB(KeyType::FORWARD, 1, 1, jlo.axes_deadzone); JLO_A_PB(KeyType::BACKWARD, 1, -1, jlo.axes_deadzone); JLO_A_PB(KeyType::LEFT, 0, 1, jlo.axes_deadzone); JLO_A_PB(KeyType::RIGHT, 0, -1, jlo.axes_deadzone); // Scroll buttons JLO_A_PB(KeyType::HOTBAR_PREV, 2, -1, jlo.axes_deadzone); JLO_A_PB(KeyType::HOTBAR_NEXT, 5, -1, jlo.axes_deadzone); return jlo; } JoystickLayout create_xbox_layout() { JoystickLayout jlo; jlo.axes_deadzone = 7000; const JoystickAxisLayout axes[JA_COUNT] = { {0, 1}, // JA_SIDEWARD_MOVE {1, 1}, // JA_FORWARD_MOVE {2, 1}, // JA_FRUSTUM_HORIZONTAL {3, 1}, // JA_FRUSTUM_VERTICAL }; memcpy(jlo.axes, axes, sizeof(jlo.axes)); // The back button means "ESC". JLO_B_PB(KeyType::ESC, 1 << 8, 1 << 8); // back JLO_B_PB(KeyType::ESC, 1 << 9, 1 << 9); // start // 4 Buttons JLO_B_PB(KeyType::JUMP, 1 << 0, 1 << 0); // A/green JLO_B_PB(KeyType::ESC, 1 << 1, 1 << 1); // B/red JLO_B_PB(KeyType::AUX1, 1 << 2, 1 << 2); // X/blue JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow // Analog Sticks JLO_B_PB(KeyType::AUX1, 1 << 11, 1 << 11); // left JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right // Triggers JLO_B_PB(KeyType::DIG, 1 << 6, 1 << 6);