|
eolib-cpp 0.1.0-beta.7
Core C++ library for writing Endless Online applications
|
This guide covers the main parts of the eolib API. Include everything with <eolib/eolib.hpp>, or include only the headers you need:
| Header | Contents |
|---|---|
| eolib/data/eo_reader.hpp, eolib/data/eo_writer.hpp | EoReader, EoWriter |
| eolib/data/number_encoder.hpp, eolib/data/string_encoder.hpp | EO number/string encoding |
| eolib/encrypt/data_encrypter.hpp, eolib/encrypt/server_verifier.hpp | Packet encryption, server verification |
| eolib/packet/packet_sequencer.hpp, eolib/packet/sequence_start.hpp | Packet sequencing |
| eolib/protocol.hpp | All generated protocol code |
| eolib/protocol/net/client.hpp, eolib/protocol/net/server.hpp | Client or server packets only |
| eolib/protocol/pub.hpp, eolib/protocol/map.hpp | Pub and map files |
Generated types derive from eolib::protocol::Serializable, which provides Serialize(EoWriter&), Deserialize(EoReader&), ByteSize() and ToString(), and can be written to a std::ostream with operator<< (unless the library is built with EOLIB_STRUCT_STRINGS=OFF). Enums have ToString(value) and operator<< too, unless the library is built with EOLIB_ENUM_STRINGS=OFF. Structs also have operator== and operator!=.
| Protocol type | C++ type |
|---|---|
| byte, char, short, three, int | int |
| bool | bool |
| string, encoded_string | std::string (raw bytes, no transcoding) |
| blob | std::vector<std::uint8_t> |
| enum | enum class : int. Unrecognized values are preserved. |
| array | std::vector<T> |
| optional field | std::optional<T> |
| switch | std::variant<std::monostate, ...> of generated case structs |
All EO numbers are int, as in eolib-dotnet and eolib-java. Sizes, positions and lengths are int too, since protocol lengths can have negative offsets applied. Only raw byte data (blob, GetBytes, pointer + length overloads) uses std::uint8_t and std::size_t.
Every packet derives from eolib::protocol::net::Packet, which adds Family() and Action(). Packet classes also have FAMILY and ACTION constants.
PacketFactory creates packets from their family and action. It returns nullptr for unknown packets:
To check whether a family and action identify a known packet without creating it, use PacketFactory::Contains:
EoReader's constructors copy (or take ownership of) the data. To read a buffer you own without copying it, use EoReader::View. The buffer must outlive the reader and its slices; generated types copy their fields out, so it only needs to live until Deserialize returns:
Switch fields are std::variant members named <field>_data. Set the alternative that matches the switch field, or serialization throws eolib::SerializationError. The generated For<Case> factories set the switch field and its data together:
There is a factory for each value of the switch field's enum:
| Case | Factory |
|---|---|
| A value whose case has data members | For<Value>(data), e.g. LoginReplyServerPacket::ForOk(data) |
| A value whose case has no data members, or that has no case | For<Value>(), e.g. LoginReplyServerPacket::ForWrongUser() |
| A value whose case contains another switch | One factory per value of the inner switch, e.g. InitInitServerPacket::ForBannedTemporary(data) |
| An integer case with data | For<CaseClass>(data), e.g. InitInitServerPacket::ForBanTypeData0(data) |
| The default case | For<Field>Default(code[, data]), e.g. AccountReplyServerPacket::ForReplyCodeDefault(code, data). Throws std::invalid_argument if code has its own case |
Integer cases without data don't get a factory, and values that are handled by the default case are created with the default factory.
To read switch data, use the generated As<Case>() accessors. They return a pointer to the case data, or nullptr if the variant holds the data for a different case, so there is no need to check the switch field as well:
Accessors are named like the factories, but only for one switch: As<Value>, As<CaseClass> for integer cases and As<Field>Default for the default case.
protocol::pub::Enf, Esf and Ecf, the protocol::pub::server files and protocol::map::Emf work the same way.
DataEncrypter transforms the packet bytes after the 2-byte length prefix in place. Each function accepts a std::vector<std::uint8_t>&, a std::string&, or a pointer and length:
A server generates a sequence start for the init handshake and tracks the sequence for each connection:
ServerVerifier::Hash(challenge) computes the response to the client's challenge in InitInitClientPacket.
The public headers compile after <windows.h> without NOMINMAX or WIN32_LEAN_AND_MEAN; a Windows test checks this. Members named like Win32 macros (for example Weight::max) are not followed by (, so function-like macros do not expand. Object-like macros from other headers can still collide with enum values. If that happens, include eolib before those headers or #undef the macro.