Hello, last week our team released the second minor version1 of Libplanet, Version 0.2. Although there have been several changes, this article will cover some key feature additions and API changes.
Introduction to Libplanet
Before getting into our updates, I haven’t introduced Libplanet on this blog, so let me briefly explain it to you.
Libplanet is a common library that solves game implementation problems such as P2P communication and data synchronization when creating online multiplay games that run on distributed P2P.
Libplanet is now being developed in C# language, with the aim of being used in conjunction with the popular Unity engine. Of course, even if you don’t use Unity, Libplanet targets .NET Standard 2.0 so that it’s easy to use for games that are implemented on .NET or Mono.
Another feature of Libplanet is that it is a library,
not an engine or a framework.
Since engines and frameworks control the entry point (
Main() method) of
a process and dictate its execution flow, game programmers have limited control
and can only program essentially through scripts within sections explictly allowed.
Libplanet does not preempt the game process and operates only when it is
explicitly invoked by the game programmer.
This allows Libplanet to function with game engines like Unity without imposing
additional limitations on the developer.
Libplanet is listed on NuGet along with API docs.
Although P2P communication was possible from Libplanet 0.1, all peers had to have a public IP. In other words, because we couldn’t reach peers behind routers, network communication was actually limited in reality. Solving this issue was a top priority for us and so traversing NAT was a major goal in the 0.2 roadmap. To cover most cases, we implemented RFC 5766 and RFC 5389, called TURN and STUN. Also, there weren’t any open source C# implementation to ease the process, so our team’s Swen Mun implemented the necessary parts of the specification from scratch. If you’re interested in Swen’s journey, please also read the article, Moving Beyond NAT, in which he explains how he solved this problem.
More Game-fitting Transaction
Transaction<T> is a unit that synchronizes data between network members.
Up to the previous version of Libplanet,
we referred to existing technologies such as Bitcoin
that solve similar problems and took on the concept
that all transactions had a sender and a recipient.
In the case of Bitcoin, it deals with monetary transactions so
the notion that there are senders and recipients in
every transaction comes naturally.
In games, however, there are often actions that do not carry a recipient
concept, such as the movement of a character,
or actions that may have more than one recipient, such as wide range skills.
So to make this transaction more game-fitting,
Libplanet from this version on will dismiss the
Recipient concept of
Transaction<T> and instead,
replace it with the
New Status Access API
IAction implementations had to request a set of account
addresses to be accessed within the
Execute() method through
Ones that attempted to read or write status of addresses that weren’t requested
in advance were treated as invalid.
However, we came to the conclusion that, since the status shared in the public network through blockchain could be read anyway, the limitation on reading didn’t mean much, only the limit on updating.
Additionally, the duplicated information on the accounts to be accessed
in both the
RequestStates() method and the
Execute() method was bug-prone.
And even if you were careful, fixing the both methods together was a big hassle.
To solve these problems, the
IAction interface’s status access API
has been greatly improved on this version of Libplanet.
RequestStates() method has disappeared altogether,
PreviousStates property of the
which entered the factor in
now provides a kind of “record of changes” API.
This “record of changes” is stacked inside the
Execute() method and
when it finally returns the change history, the status is then updated.
Also, when a transaction is created, the action is executed in “rehearsal mode,”
which obtains a set of addresses that the
Execute() method is
trying to update.
The address set is then included in the transaction with a signature.
This prevents a recipient node from changing the account status of addresses
not included in the address set of the signed transaction.
Optional Subtype Polymorphism
Up to the previous version, the only usage of Libplanet actions was that
each game defines an abstract classs which implements
has multiple concrete classes inheriting it.
But depending on the game, there are cases where it might be better to implement
IAction as an only class and select a behavior based on data that goes into
the action than to define multiple types of action at the
Moreover, some projects might face difficulties because the dynamic dispatcher
IAction types is internally implemented using .NET reflection.
Hence, from this version of Libplanet,
Transaction<T> needs not only to
IAction but also to concrete classes.
Abstraction classes or interfaces aren’t acceptable
even if they implement
and the presence of subtypes is completely ignored.
Instead, if you want to select the behavior of an action through
subtype polymorphism, you can use
PolymorphicAction<T>, a new action class to
decorate another action.
For example, changing
Transaction<PolymorphicAction<AbstractionAction>> will work as it has been
in most cases.
Of course, the
PolymorphicAction<T> class uses .NET reflection under the hood.
There have been lots of other changes on Libplanet 0.2.0, so check them out in our release notes.
FYI, two days after the release of 0.2.0, a new version with some troubleshooting issues is now released, with the latest version being 0.2.1 (as of April 9, 2019).
If you’re curious, install and have a look around. And If you have any questions, please join our Discord chatroom!
We have not yet released a major version. ↩︎