magicnet


Namemagicnet JSON
Version 0.0.3 PyPI version JSON
download
home_page
SummaryMagicNet is a modern and flexible networking library
upload_time2023-12-16 17:50:24
maintainer
docs_urlNone
author
requires_python>=3.10
license
keywords astron networking
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ***MagicNet** — A fun and flexible networking library for Python applications.*

---

Source code: <https://github.com/wizzerinus/magicnet>

---

## Introduction

MagicNet is a modern library for building real-time web applications.

Its key features are:

* **Client simplicity**: write simple and intuitive Python code, typehint your code if you want
  (strongly recommended to use typehints with the object-based API but still optional),
  write zero boilerplate, and get efficient network interaction in any scenario.
* **Client flexibility**:
  * MagicNet makes no assumptions about the networking stack of your application.
    Many libraries require a specific networking stack (i.e. AsyncIO), which may not at all
    work with a different event loop.
  * MagicNet's low level functionality is completely decoupled from the high-level functionality.
    Most of the time, changing the networking stack is as simple as changing one variable
    indicating the way to send pure bytestrings. The application code itself
    does not need to care about the networking stack or event loop used.
  * MagicNet packs both a low-level Message API and a high-level Object API (see examples below).
    This allows writing domain-specific code in the way that is best suited for a certain problem.
* **Server flexibility**: MagicNet can be used with a simple client-server structure,
  as well as a more complicated structure with multiple servers, proxies, etc.
  Proxies themselves can be also implemented in the same library using the
  transport middleware system.
* **Fast prototyping:** MagicNet can be used with a Native connection protocol,
  which merges all parts of the application into one process without changing
  the internal functionality. Any client code that works on this protocol
  is almost guaranteed to work on any different protocol.
* **Simple protocol**: If any server is slower than desired, that server can be rewritten
  in a different language (such as Go or Rust) without touching the rest of infrastructure,
  or the application-specific code used.

---

## Requirements

Requires Python 3.10+. 

The default installation of MagicNetworking has no dependencies and can be used out of the box,
however, it is recommended to install `magicnet[standard]` which includes the following:

* Msgpack, for more efficient message packager.

---

## Example

MagicNet packages two main APIs — the Message-level API (procedural)
and the Object-level API (object-oriented). Both APIs work on the same
underlying networking stack and are useful in different scenarios:
globally used methods are likely better to put into the Message API,
while methods specific to an object are likely better as an object API.

### Running provided examples

Full-fledged examples can be found in `examples` directory.

* Native connection: run `a_native_connection.py`.
  * Note that this example will immediately exit after sending a few messages.
* Client-server basics: run `b_server.py`, then `b_client.py` or `b_panda3d_client.py`
  * The Panda3D client requires a reasonably recent version of Panda3D.
* Network objects: run `c_network_objects/c_marshal_things.py`, then `c_network_objects/c_server.py`,
  then `c_network_objects/c_client.py`.

### Message API

```python
from magicnet.core.net_message import NetMessage
from magicnet.protocol import network_types
from magicnet.protocol.processor_base import MessageProcessor

# 0-63 are reserved for builtin message IDs
# Custom message IDs can start with 64
MSG_CUSTOM = 64
MSG_RESPONSE = 65

class MsgCustom(MessageProcessor):
    arg_type = tuple[network_types.s256]
    # If typehints are used and the MessageValidation middleware
    # is used, messages that do not match the typehints are rejected

    def invoke(self, message: NetMessage):
        # this will be called on the server, as the client sends this message
        client_name = message.sent_from.context.get("username")
        if not client_name:
            message.disconnect_sender(10, "MsgCustom requires a username!")
            return

        print(f"Client {client_name} sent:", message.parameters[0])
        to_send = NetMessage(
            MSG_RESPONSE,
            ("".join(reversed(message.parameters[0])), ),
            # uncomment if you want only the sender to receive the message
            # destination=message.sent_from,
        )
        self.manager.send_message(to_send)

class MsgCustomResponse(MessageProcessor):
    arg_type = tuple[network_types.s256]

    def invoke(self, message: NetMessage):
        # this will be called on the client, as the server sends this message
        print("Reversed message:", message.parameters[0])

custom_messages = {MSG_CUSTOM: MsgCustom, MSG_RESPONSE: MsgCustomResponse}

# define the server parameters in one script...
# define the client parameters in another or the same script...
client = ...
msg = NetMessage(MSG_CUSTOM, ["some string"])
client.send(msg)
```

### Object API

```python
import dataclasses

from magicnet.netobjects.network_object import NetworkObject
from magicnet.netobjects.network_field import NetworkField
from magicnet.protocol import network_types

# On the server
@dataclasses.dataclass
class NetworkNumberServer(NetworkObject):
    network_name = "one-number"
    object_role = 1
    value: int = dataclasses.field(init=False, default=0)

    def net_create(self):
        pass

    def net_delete(self):
        pass

    @NetworkField
    def set_init_value(self, value: network_types.int32):
        # If typehints are used, all messages that do not pass the typehints are rejected
        # Note: only a subset of typehints is supported due to the difficulty
        # of encoding a typehint as a string (which is required to i.e.
        # exclude server classes from being imported in the client)
        self.value = value
        self.send_message("set_current_value", [self.value])

    @NetworkField
    def add_value(self, value: network_types.int32):
        self.value += value
        # Not validating integer overflow in this example, for simplicity
        # (the server won't crash, just the number won't be sent back)
        self.send_message("set_current_value", [self.value])

# On the client
@dataclasses.dataclass
class NetworkNumberClient(NetworkObject):
    network_name = "one-number"
    object_role = 0
    value: int = dataclasses.field(init=False, default=0)

    def net_create(self):
        # This runs when the object was created
        self.send_message("add_value", [15])

    def net_delete(self):
        pass

    @NetworkField
    def set_current_value(self, value: network_types.int32):
        print(f"Object's value changed: {self.value} -> {value}")
        self.value = value

# Creating the object
# The API is the same on the server and the client side, although
# the semantics of the network datagrams used differ
client = ...
obj = NetworkNumberClient(controller=client)
obj.send_message("set_init_value", [10])
obj.request_generate()
```

---

## Component information

This is the list of all components I would want to implement, but not all of them are done yet.
The components that can be implemented easily but are not in the standard library
due to being opinionated are marked as domain-specific.

### Object API

* **Generation and removal of objects** - implemented
* **Network fields** - implemented
* **Field type validation** - implemented
* **Passing structs as arguments** - implemented
* **RAM persistence of fields** - implemented
* **Database persistence** - not implemented
  * Most likely, I will implement three backends (SQL, MongoDB, dbm for local development).
* **Object API routing** - implemented
  * Zone-based message routing is currently implemented (each client "sees" a set of zones,
    and each object is in exactly one zone). The set of visible zones can be configured
    through shared parameters (i.e., on handle-to-handle basis).

### Message and Connection Layer

* **Handshake** - fully functional
* **Shared parameters** - fully functional
  * Includes a way to save data on a connection that is shared between both sides.
    Note that there is not a built in security protocol for this. 
* **Middleware servers** - not implemented
  * This includes, for example, a server that merely routes datagrams,
    instead of processing it immediately. This would require somehow packing the
    connection into the datagram and making virtual connections on both sides.
    For the usecases of this, see below "High-load scenarios".
* **Connection transfer** - not implemented
  * This is useful for High-load scenarios as well as reconnection.
* **Reconnection** - not implemented
  * As reconnection cannot be implemented on some stacks like TCP,
    it really is just "making a new connection to the same server without losing state".
    This is application logic-dependent and may be easier than I think it is.
* **Custom messages** - fully implemented

### Network Layer

This part is really annoying due to a completely unavoidable combinatoric explosion
of connection types and event loops being in use (two in the standard library and many
other event loops made by other libraries).

* **In-memory transfer (server and client in the same process)** - functional
  * This may sound stupid but it's actually really nice for local prototyping!
* **AsyncIO/TCP combination** - functional
* **AsyncIO/UDP combination** - not done
* **AsyncIO/Websockets combination** - not done
* **Threads/TCP combination** - not done
* **Threads/UDP combination** - not done
* **Threads/Websockets combination** - not done

* **Bonus: Panda3D/TCP combination** - functional but not included in the main distribution
  * Currently this may be downloaded from the `examples` folder and used mostly as-is.

### Security Layer

* **Message API typechecking** - functional
  * Requires the use of `MessageValidatorMiddleware`
    (recommended to have in all setups in a bridge or server node
     to prevent malicious clients from crashing a server)
* **Object API typechecking** - fully functional
  * Available out of the box
* **Message API permissions** - domain-specific
  * Requires the use of a custom TransportMiddleware
* **Object API permissions** - domain-specific
  * Requires the use of a custom event handler (`MNMathTargets.FIELD_CALL_ALLOWED`)
* **Message API routing** - domain-specific 
  * Requires the use of a custom TransportManager or HandleFilter
* **Message-level security** - partially implemented / domain-specific
  * This includes things like "Reject MOTD from the clients" and "Reject unauthenticated messages".
    Those are currently implemented.
  * This also includes things like "Encrypt all messages". This is a domain-specific area -
    everything can be done through middlewares, and also goes out of the scope of the library.
* **Protocol-level security** - not implemented
  * This includes things like
    "Prevent a client from sending 4 billion bytes in one socket operation" and
    "Prevent 100000 clients from being created on the same IP".
    I am not totally sure how do prevent this, anyway, and am open to suggestions.

### Different scenarios

* **Local development** - functional
* **Single all-to-all communication** - functional
* **High-load scenarios** - not implemented
  * For high-load scenarios, sending any message that is not pointing at a certain
    client, it will be routed to every single connected client. This is quite slow
    in Python. For these, I recommend making a middleware server, so that the server talks
    to the middleware, the clients talk to the middleware, and the server is behind
    a firewall so it can't be directly connected to. Unfortunately this does not fix
    all of the problems of such setups unless the middleware is written in a language like
    Rust, which goes beyond the scope of this library.
  * Another possible remedy is to have a middleware chain acting as a load balancer. For example,
    the server talks to 10 middlewares, and each client connection is sent to one of them.
    This reduces the load substantially, allowing better scaling, but is significantly harder
    to implement. It also requires an ability to "transfer" a connection from one pair of clients
    to another pair of clients (see above).

---

## Networking stacks

The defining power of MagicNet is being able to work with different networking stacks.
Out of the box are included an AsyncIO-TCP-based stack, and a native single-process stack.
An example of a custom networking stack is included in the example `b_client_panda3d`:
only the networking adapter (a very small fraction of the application code in real applications)
had to be changed to migrate to a completely different asynchronous stack
with no AsyncIO knowledge whatsoever.

The same way, applications can be migrated from one launching type into another
without making any changes to the application code. For example, if a game has
proper client-server separation (which is made easier by the internal structure of the library),
the server can be normally run in the same process using the native transport handler.
The game then can add a "local server" feature opening a socket-based connection
with other players, while keeping the native handler for the first player intact.
If the server has to be extracted into a separate application, the only thing that
has to be changed is the transport handler between the first player and the server,
with the entire game client and server code remaining intact!

---

## Caveats

* This project is a work-in-progress, and things may change at any time.
  More importantly, many components have not been implemented yet.
* Things like Trio/Curio/etc.-based event loops and network transports
  will not be implemented in the main library. (On the flip side, those aren't too hard to write
  if you need them in your application!)
* I am not a security specialist, so some fatal mistakes may have slipped through.
  Please report any security issues you find on the issue tracker so I can fix them!
* The message processing is synchronous, as this library has to work in both synchronous
  and asynchronous contexts. This means if your messages require something like database interaction,
  it will be harder to do efficiently without callback hell (synchronous database handlers 
  tend to be quite slow). I do not currently have a solution in mind,
  but will implement one if a reasonably good one is suggested.

---

## Comparison with similar libraries

### gRPC

gRPC is a very popular library for building networked applications. The scope of gRPC is
quite different from MagicNet, however:

* Any message in gRPC is sent from the client, which will then expect a response.
  This means the library is not suitable for real-time applications, which will often
  send messages from the server to the client, and messages don't have a response
  (which can be simulated by sending a message back).
* For gRPC language compatibility is very important. While the protocol of MagicNet
  is language-agnostic, the library itself is written in Python and will require additional work
  to be used in other languages.
* gRPC's protocol has to be configured through Protobuf. While this is important for language
  compatibility, it is less intuitive than using typehints.

### Astron

Astron is a library for building real-time applications, originally made for Disney's Toontown Online.
MagicNet is inspired by Astron in many ways, as I have worked with Astron for a long time.
(In fact, the reason why MagicNet was created is because there was not a powerful enough networking
library that worked with Panda3D!) That said there are some key differences:

* The core of Astron is written in C++, while MagicNet is written in Python.
  This means that Astron is much faster, but also much harder to use.
  (This can be somewhat remedied by rewriting the core of the library in a modern language, which is planned.)
* Astron currently doesn't have a free (as in, not proprietary) client implementation.
  The CMU protocol implemented in Panda3D does not incorporate many of the features of the Astron protocol,
  such as Distributed objects. In addition, the Panda3D implementation only really works
  with the event loop of that engine, which means AsyncIO cannot be used easily.
* Adding custom message codes to most Astron implementations is relatively difficult due to the
  way the software is designed. MagicNet is designed to be as flexible as possible in this regard.
* The distributed fields have to be configured through DC files, which are not very intuitive.
  MagicNet uses typehints instead, which are much easier to use.

In general Astron is quite archaic and tends itself to be used in a very specific way
(mostly creating MMORPG games) due to the complexity of the server component, although it's quite good at that.
MagicNet is better suitable for smaller projects, and is much easier to use.


            

Raw data

            {
    "_id": null,
    "home_page": "",
    "name": "magicnet",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": "",
    "keywords": "astron,networking",
    "author": "",
    "author_email": "Wizzerinus <quillinde@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/db/03/97bdb55445ef56df7ebfa32fa095afb3f1d37378bbe1e74e78a7df4fe9fe/magicnet-0.0.3.tar.gz",
    "platform": null,
    "description": "***MagicNet** \u2014 A fun and flexible networking library for Python applications.*\n\n---\n\nSource code: <https://github.com/wizzerinus/magicnet>\n\n---\n\n## Introduction\n\nMagicNet is a modern library for building real-time web applications.\n\nIts key features are:\n\n* **Client simplicity**: write simple and intuitive Python code, typehint your code if you want\n  (strongly recommended to use typehints with the object-based API but still optional),\n  write zero boilerplate, and get efficient network interaction in any scenario.\n* **Client flexibility**:\n  * MagicNet makes no assumptions about the networking stack of your application.\n    Many libraries require a specific networking stack (i.e. AsyncIO), which may not at all\n    work with a different event loop.\n  * MagicNet's low level functionality is completely decoupled from the high-level functionality.\n    Most of the time, changing the networking stack is as simple as changing one variable\n    indicating the way to send pure bytestrings. The application code itself\n    does not need to care about the networking stack or event loop used.\n  * MagicNet packs both a low-level Message API and a high-level Object API (see examples below).\n    This allows writing domain-specific code in the way that is best suited for a certain problem.\n* **Server flexibility**: MagicNet can be used with a simple client-server structure,\n  as well as a more complicated structure with multiple servers, proxies, etc.\n  Proxies themselves can be also implemented in the same library using the\n  transport middleware system.\n* **Fast prototyping:** MagicNet can be used with a Native connection protocol,\n  which merges all parts of the application into one process without changing\n  the internal functionality. Any client code that works on this protocol\n  is almost guaranteed to work on any different protocol.\n* **Simple protocol**: If any server is slower than desired, that server can be rewritten\n  in a different language (such as Go or Rust) without touching the rest of infrastructure,\n  or the application-specific code used.\n\n---\n\n## Requirements\n\nRequires Python 3.10+. \n\nThe default installation of MagicNetworking has no dependencies and can be used out of the box,\nhowever, it is recommended to install `magicnet[standard]` which includes the following:\n\n* Msgpack, for more efficient message packager.\n\n---\n\n## Example\n\nMagicNet packages two main APIs \u2014 the Message-level API (procedural)\nand the Object-level API (object-oriented). Both APIs work on the same\nunderlying networking stack and are useful in different scenarios:\nglobally used methods are likely better to put into the Message API,\nwhile methods specific to an object are likely better as an object API.\n\n### Running provided examples\n\nFull-fledged examples can be found in `examples` directory.\n\n* Native connection: run `a_native_connection.py`.\n  * Note that this example will immediately exit after sending a few messages.\n* Client-server basics: run `b_server.py`, then `b_client.py` or `b_panda3d_client.py`\n  * The Panda3D client requires a reasonably recent version of Panda3D.\n* Network objects: run `c_network_objects/c_marshal_things.py`, then `c_network_objects/c_server.py`,\n  then `c_network_objects/c_client.py`.\n\n### Message API\n\n```python\nfrom magicnet.core.net_message import NetMessage\nfrom magicnet.protocol import network_types\nfrom magicnet.protocol.processor_base import MessageProcessor\n\n# 0-63 are reserved for builtin message IDs\n# Custom message IDs can start with 64\nMSG_CUSTOM = 64\nMSG_RESPONSE = 65\n\nclass MsgCustom(MessageProcessor):\n    arg_type = tuple[network_types.s256]\n    # If typehints are used and the MessageValidation middleware\n    # is used, messages that do not match the typehints are rejected\n\n    def invoke(self, message: NetMessage):\n        # this will be called on the server, as the client sends this message\n        client_name = message.sent_from.context.get(\"username\")\n        if not client_name:\n            message.disconnect_sender(10, \"MsgCustom requires a username!\")\n            return\n\n        print(f\"Client {client_name} sent:\", message.parameters[0])\n        to_send = NetMessage(\n            MSG_RESPONSE,\n            (\"\".join(reversed(message.parameters[0])), ),\n            # uncomment if you want only the sender to receive the message\n            # destination=message.sent_from,\n        )\n        self.manager.send_message(to_send)\n\nclass MsgCustomResponse(MessageProcessor):\n    arg_type = tuple[network_types.s256]\n\n    def invoke(self, message: NetMessage):\n        # this will be called on the client, as the server sends this message\n        print(\"Reversed message:\", message.parameters[0])\n\ncustom_messages = {MSG_CUSTOM: MsgCustom, MSG_RESPONSE: MsgCustomResponse}\n\n# define the server parameters in one script...\n# define the client parameters in another or the same script...\nclient = ...\nmsg = NetMessage(MSG_CUSTOM, [\"some string\"])\nclient.send(msg)\n```\n\n### Object API\n\n```python\nimport dataclasses\n\nfrom magicnet.netobjects.network_object import NetworkObject\nfrom magicnet.netobjects.network_field import NetworkField\nfrom magicnet.protocol import network_types\n\n# On the server\n@dataclasses.dataclass\nclass NetworkNumberServer(NetworkObject):\n    network_name = \"one-number\"\n    object_role = 1\n    value: int = dataclasses.field(init=False, default=0)\n\n    def net_create(self):\n        pass\n\n    def net_delete(self):\n        pass\n\n    @NetworkField\n    def set_init_value(self, value: network_types.int32):\n        # If typehints are used, all messages that do not pass the typehints are rejected\n        # Note: only a subset of typehints is supported due to the difficulty\n        # of encoding a typehint as a string (which is required to i.e.\n        # exclude server classes from being imported in the client)\n        self.value = value\n        self.send_message(\"set_current_value\", [self.value])\n\n    @NetworkField\n    def add_value(self, value: network_types.int32):\n        self.value += value\n        # Not validating integer overflow in this example, for simplicity\n        # (the server won't crash, just the number won't be sent back)\n        self.send_message(\"set_current_value\", [self.value])\n\n# On the client\n@dataclasses.dataclass\nclass NetworkNumberClient(NetworkObject):\n    network_name = \"one-number\"\n    object_role = 0\n    value: int = dataclasses.field(init=False, default=0)\n\n    def net_create(self):\n        # This runs when the object was created\n        self.send_message(\"add_value\", [15])\n\n    def net_delete(self):\n        pass\n\n    @NetworkField\n    def set_current_value(self, value: network_types.int32):\n        print(f\"Object's value changed: {self.value} -> {value}\")\n        self.value = value\n\n# Creating the object\n# The API is the same on the server and the client side, although\n# the semantics of the network datagrams used differ\nclient = ...\nobj = NetworkNumberClient(controller=client)\nobj.send_message(\"set_init_value\", [10])\nobj.request_generate()\n```\n\n---\n\n## Component information\n\nThis is the list of all components I would want to implement, but not all of them are done yet.\nThe components that can be implemented easily but are not in the standard library\ndue to being opinionated are marked as domain-specific.\n\n### Object API\n\n* **Generation and removal of objects** - implemented\n* **Network fields** - implemented\n* **Field type validation** - implemented\n* **Passing structs as arguments** - implemented\n* **RAM persistence of fields** - implemented\n* **Database persistence** - not implemented\n  * Most likely, I will implement three backends (SQL, MongoDB, dbm for local development).\n* **Object API routing** - implemented\n  * Zone-based message routing is currently implemented (each client \"sees\" a set of zones,\n    and each object is in exactly one zone). The set of visible zones can be configured\n    through shared parameters (i.e., on handle-to-handle basis).\n\n### Message and Connection Layer\n\n* **Handshake** - fully functional\n* **Shared parameters** - fully functional\n  * Includes a way to save data on a connection that is shared between both sides.\n    Note that there is not a built in security protocol for this. \n* **Middleware servers** - not implemented\n  * This includes, for example, a server that merely routes datagrams,\n    instead of processing it immediately. This would require somehow packing the\n    connection into the datagram and making virtual connections on both sides.\n    For the usecases of this, see below \"High-load scenarios\".\n* **Connection transfer** - not implemented\n  * This is useful for High-load scenarios as well as reconnection.\n* **Reconnection** - not implemented\n  * As reconnection cannot be implemented on some stacks like TCP,\n    it really is just \"making a new connection to the same server without losing state\".\n    This is application logic-dependent and may be easier than I think it is.\n* **Custom messages** - fully implemented\n\n### Network Layer\n\nThis part is really annoying due to a completely unavoidable combinatoric explosion\nof connection types and event loops being in use (two in the standard library and many\nother event loops made by other libraries).\n\n* **In-memory transfer (server and client in the same process)** - functional\n  * This may sound stupid but it's actually really nice for local prototyping!\n* **AsyncIO/TCP combination** - functional\n* **AsyncIO/UDP combination** - not done\n* **AsyncIO/Websockets combination** - not done\n* **Threads/TCP combination** - not done\n* **Threads/UDP combination** - not done\n* **Threads/Websockets combination** - not done\n\n* **Bonus: Panda3D/TCP combination** - functional but not included in the main distribution\n  * Currently this may be downloaded from the `examples` folder and used mostly as-is.\n\n### Security Layer\n\n* **Message API typechecking** - functional\n  * Requires the use of `MessageValidatorMiddleware`\n    (recommended to have in all setups in a bridge or server node\n     to prevent malicious clients from crashing a server)\n* **Object API typechecking** - fully functional\n  * Available out of the box\n* **Message API permissions** - domain-specific\n  * Requires the use of a custom TransportMiddleware\n* **Object API permissions** - domain-specific\n  * Requires the use of a custom event handler (`MNMathTargets.FIELD_CALL_ALLOWED`)\n* **Message API routing** - domain-specific \n  * Requires the use of a custom TransportManager or HandleFilter\n* **Message-level security** - partially implemented / domain-specific\n  * This includes things like \"Reject MOTD from the clients\" and \"Reject unauthenticated messages\".\n    Those are currently implemented.\n  * This also includes things like \"Encrypt all messages\". This is a domain-specific area -\n    everything can be done through middlewares, and also goes out of the scope of the library.\n* **Protocol-level security** - not implemented\n  * This includes things like\n    \"Prevent a client from sending 4 billion bytes in one socket operation\" and\n    \"Prevent 100000 clients from being created on the same IP\".\n    I am not totally sure how do prevent this, anyway, and am open to suggestions.\n\n### Different scenarios\n\n* **Local development** - functional\n* **Single all-to-all communication** - functional\n* **High-load scenarios** - not implemented\n  * For high-load scenarios, sending any message that is not pointing at a certain\n    client, it will be routed to every single connected client. This is quite slow\n    in Python. For these, I recommend making a middleware server, so that the server talks\n    to the middleware, the clients talk to the middleware, and the server is behind\n    a firewall so it can't be directly connected to. Unfortunately this does not fix\n    all of the problems of such setups unless the middleware is written in a language like\n    Rust, which goes beyond the scope of this library.\n  * Another possible remedy is to have a middleware chain acting as a load balancer. For example,\n    the server talks to 10 middlewares, and each client connection is sent to one of them.\n    This reduces the load substantially, allowing better scaling, but is significantly harder\n    to implement. It also requires an ability to \"transfer\" a connection from one pair of clients\n    to another pair of clients (see above).\n\n---\n\n## Networking stacks\n\nThe defining power of MagicNet is being able to work with different networking stacks.\nOut of the box are included an AsyncIO-TCP-based stack, and a native single-process stack.\nAn example of a custom networking stack is included in the example `b_client_panda3d`:\nonly the networking adapter (a very small fraction of the application code in real applications)\nhad to be changed to migrate to a completely different asynchronous stack\nwith no AsyncIO knowledge whatsoever.\n\nThe same way, applications can be migrated from one launching type into another\nwithout making any changes to the application code. For example, if a game has\nproper client-server separation (which is made easier by the internal structure of the library),\nthe server can be normally run in the same process using the native transport handler.\nThe game then can add a \"local server\" feature opening a socket-based connection\nwith other players, while keeping the native handler for the first player intact.\nIf the server has to be extracted into a separate application, the only thing that\nhas to be changed is the transport handler between the first player and the server,\nwith the entire game client and server code remaining intact!\n\n---\n\n## Caveats\n\n* This project is a work-in-progress, and things may change at any time.\n  More importantly, many components have not been implemented yet.\n* Things like Trio/Curio/etc.-based event loops and network transports\n  will not be implemented in the main library. (On the flip side, those aren't too hard to write\n  if you need them in your application!)\n* I am not a security specialist, so some fatal mistakes may have slipped through.\n  Please report any security issues you find on the issue tracker so I can fix them!\n* The message processing is synchronous, as this library has to work in both synchronous\n  and asynchronous contexts. This means if your messages require something like database interaction,\n  it will be harder to do efficiently without callback hell (synchronous database handlers \n  tend to be quite slow). I do not currently have a solution in mind,\n  but will implement one if a reasonably good one is suggested.\n\n---\n\n## Comparison with similar libraries\n\n### gRPC\n\ngRPC is a very popular library for building networked applications. The scope of gRPC is\nquite different from MagicNet, however:\n\n* Any message in gRPC is sent from the client, which will then expect a response.\n  This means the library is not suitable for real-time applications, which will often\n  send messages from the server to the client, and messages don't have a response\n  (which can be simulated by sending a message back).\n* For gRPC language compatibility is very important. While the protocol of MagicNet\n  is language-agnostic, the library itself is written in Python and will require additional work\n  to be used in other languages.\n* gRPC's protocol has to be configured through Protobuf. While this is important for language\n  compatibility, it is less intuitive than using typehints.\n\n### Astron\n\nAstron is a library for building real-time applications, originally made for Disney's Toontown Online.\nMagicNet is inspired by Astron in many ways, as I have worked with Astron for a long time.\n(In fact, the reason why MagicNet was created is because there was not a powerful enough networking\nlibrary that worked with Panda3D!) That said there are some key differences:\n\n* The core of Astron is written in C++, while MagicNet is written in Python.\n  This means that Astron is much faster, but also much harder to use.\n  (This can be somewhat remedied by rewriting the core of the library in a modern language, which is planned.)\n* Astron currently doesn't have a free (as in, not proprietary) client implementation.\n  The CMU protocol implemented in Panda3D does not incorporate many of the features of the Astron protocol,\n  such as Distributed objects. In addition, the Panda3D implementation only really works\n  with the event loop of that engine, which means AsyncIO cannot be used easily.\n* Adding custom message codes to most Astron implementations is relatively difficult due to the\n  way the software is designed. MagicNet is designed to be as flexible as possible in this regard.\n* The distributed fields have to be configured through DC files, which are not very intuitive.\n  MagicNet uses typehints instead, which are much easier to use.\n\nIn general Astron is quite archaic and tends itself to be used in a very specific way\n(mostly creating MMORPG games) due to the complexity of the server component, although it's quite good at that.\nMagicNet is better suitable for smaller projects, and is much easier to use.\n\n",
    "bugtrack_url": null,
    "license": "",
    "summary": "MagicNet is a modern and flexible networking library",
    "version": "0.0.3",
    "project_urls": {
        "bugtracker": "https://github.com/wizzerinus/magicnet/issues",
        "homepage": "https://github.com/wizzerinus/magicnet"
    },
    "split_keywords": [
        "astron",
        "networking"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "8cfcdfbcce48895d15bfea6454214da0925715862edccfc2587b511424d704e5",
                "md5": "936bf5b0acdab6ba3c3885eae8a83e5f",
                "sha256": "06c83e954b74a868555efcc4c8d5fe00c748f483135b31937c56603c4e2eef96"
            },
            "downloads": -1,
            "filename": "magicnet-0.0.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "936bf5b0acdab6ba3c3885eae8a83e5f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 56931,
            "upload_time": "2023-12-16T17:50:22",
            "upload_time_iso_8601": "2023-12-16T17:50:22.935966Z",
            "url": "https://files.pythonhosted.org/packages/8c/fc/dfbcce48895d15bfea6454214da0925715862edccfc2587b511424d704e5/magicnet-0.0.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "db0397bdb55445ef56df7ebfa32fa095afb3f1d37378bbe1e74e78a7df4fe9fe",
                "md5": "fbecc97c4e42035978b9584ca2fe810e",
                "sha256": "4ea63a47882152b36a18020ad0a73e385f3da840c00e4da72814ad59e582c736"
            },
            "downloads": -1,
            "filename": "magicnet-0.0.3.tar.gz",
            "has_sig": false,
            "md5_digest": "fbecc97c4e42035978b9584ca2fe810e",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 39090,
            "upload_time": "2023-12-16T17:50:24",
            "upload_time_iso_8601": "2023-12-16T17:50:24.868922Z",
            "url": "https://files.pythonhosted.org/packages/db/03/97bdb55445ef56df7ebfa32fa095afb3f1d37378bbe1e74e78a7df4fe9fe/magicnet-0.0.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-12-16 17:50:24",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "wizzerinus",
    "github_project": "magicnet",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "magicnet"
}
        
Elapsed time: 0.16119s