项目作者: nosix

项目描述 :
Multiplay server of Minecraft Pocket Edition
高级语言: Python
项目地址: git://github.com/nosix/PyMineHub.git
创建时间: 2017-11-19T11:55:09Z
项目社区:https://github.com/nosix/PyMineHub

开源协议:GNU Lesser General Public License v3.0

下载


This project is finished. There are no plans to update in the future. If you need to update, please use fork.

PyMineHub (PMH)

Multiplay server of Minecraft Pocket Edition

The goal of this project is to make the server small and to work with others.
Extensibility than full spec implementation.
This project will assume that MineCraft is used as an environment of creation, not as a game.

Target version

  • Python 3.5.3
    • 2017-09-07-raspbian-stretch-lite.img
  • and later

Testing environment

  • Python 3.6.1
    • macOS High Sierra 10.13
  • Python 3.5.3
    • Raspbian stretch lite 2017-09-07

Demo

demo1

demo2

Trial

First, get the source code and start the server.

  1. $ git clone https://github.com/nosix/PyMineHub.git
  2. $ cd PyMineHub
  3. PyMineHub $ bin/run.sh
  4. In your environment, Python 3.6.1 is used.
  5. INFO:pyminehub.config:RAKNET_SERVER_PORT=19132
  6. INFO:pyminehub.config:TCP_SERVER_PORT=19142
  7. INFO:pyminehub.config:WORLD_NAME=PyMineHub
  8. INFO:pyminehub.config:GAME_MODE=SURVIVAL
  9. INFO:pyminehub.config:SPAWN_MOB=True
  10. INFO:pyminehub.config:INIT_SPACE=(32, 32)
  11. INFO:pyminehub.config:PLAYER_SPAWN_POSITION=(256, 56, 256)
  12. INFO:pyminehub.mcpe.plugin.loader:Plugin is loaded from directory "plugin".
  13. INFO:pyminehub.mcpe.plugin.loader:AboutCommandProcessor was registered into CommandRegistry.
  14. INFO:pyminehub.mcpe.plugin.loader:LevelDBChunkGeneratorPlugin overrode ChunkGeneratorPlugin.
  15. INFO:pyminehub.mcpe.plugin.loader:ActionCommandProcessor was registered into CommandRegistry.
  16. INFO:pyminehub.mcpe.plugin.loader:GameEventCommandProcessor was registered into CommandRegistry.

Sign-in from Friends tab on MineCraft.
PyMineHub support MineCraft version 1.2.7 (maybe, later versions as well) and protocol version 160.
We have confirmed working at version 1.2.10.

When you start the game, a world of only water is generated.
And, birds move randomly.
If you want to change the generated world and mob movements, use the plugin.
By default, the source code under the plugin directory in the current directory is loaded.
If you want to change the plugin directory, specify the directory in the MPH_PLUGIN_ROOT environment variable.
To disable the plugin, specify the MPH_PLUGIN_ROOT environment variable to be "".
Run run.sh with the -d option to disable the plugin.

  1. PyMineHub $ rm PyMineHub-p0.db
  2. PyMineHub $ bin/run.sh -d
  3. In your environment, Python 3.6.1 is used.
  4. INFO:pyminehub.config:RAKNET_SERVER_PORT=19132
  5. INFO:pyminehub.config:TCP_SERVER_PORT=19142
  6. INFO:pyminehub.config:WORLD_NAME=PyMineHub
  7. INFO:pyminehub.config:GAME_MODE=SURVIVAL
  8. INFO:pyminehub.config:SPAWN_MOB=True
  9. INFO:pyminehub.config:INIT_SPACE=(32, 32)
  10. INFO:pyminehub.config:PLAYER_SPAWN_POSITION=(256, 56, 256)
  11. INFO:pyminehub.mcpe.plugin.loader:PMH_PLUGIN_ROOT=
  12. INFO:pyminehub.mcpe.plugin.loader:Plugin is disabled.

PyMineHub-p0.db is a data file.
Currently SQLite DB is used, but we might change it (the timing is undecided).
If INIT_SPACE is specified,
it creates chunks (parts of terrain data) in the area specified at startup
and duplicates chunks in this area to other area.
If INIT_SPACE is None, chunks are generated when they are needed.
This is a feature to lower the CPU load during play.

Again, sign-in from Friends tab on MineCraft.
When you start the game, a world of only grass is generated. And, birds move randomly.
In this example, the plugin directories are not loaded and the default plugins are used.
The default plugins are in src/pyminehub/mcpe/plugin/default.
Then, the interface of the plugins are defined in src/pyminehub/mcpe/plugin.

  • ChunkGeneratorPlugin
    • override
  • MobProcessorPlugin
    • override
  • PlayerConfigPlugin
    • override
  • WorldExtensionPlugin
    • append
  • ExtraCommandPlugin
    • append

When running run.sh without the -d option (first example),
LevelDBChunkGeneratorPlugin in plugin directory override the default plugin.
In the case of overloading, the last loaded plugin is enabled.
On the other hand, more than one ExtraCommandPlugin will be added, all loaded plugins are enabled.

Plugin

The plugin loader imports the module directly under the plugin directory,
and it reads the class described in those __all__.
Classes need to have plugin’s interface in superclass.
Deleting the description of __all__ will disable the plugin.

  1. plugin (or directory specified by MPH_PLUGIN_ROOT)
  2. xxx_plugin
  3. __init__.py
  4. __all__ = [...]
  5. yyy_plugin
  6. __init__.py
  7. __all__ = [...]

ChunkGeneratorPlugin

This plugin is a plugin for generating terrain data.

MobProcessorPlugin

This plugin is a plugin for controlling the appearance, movement, and extinction of mobs.
The contents that can be done if there is a demand may increase.

PlayerConfigPlugin

This plugin is a plugin for setting for each player.
Although we have set it for extension, we recommend that you do not want to use the current situation.
The contents that can be done if there is demand may increase.

WorldExtensionPlugin

This plugin customizes the update of the world.
You will be able to do the following.

  • Change or cancel the received update instruction (filter_action)
  • Change or cancel update notification to be sent (filter_event)
  • Periodically generate update instructions (update)

All update instructions received from the client and all update notifications sent from the world server,
it passes through filter of all loaded WorldExtensionPlugin.

  1. Client -> (action a) -> PluginA.filter_action -> (action a') -> PluginB.filter_action -> (action a') -> World Server
  2. Client <- (event e') <- PluginA.filter_event <- (event e') <- PluginB.filter_event <- (event e) <- World Server
  3. action a : original update instruction
  4. action a' : modified update instruction
  5. event e : original update notification
  6. event e' : modified update notification

It can cancel, when filter return None.

  1. Client -> (action a) -> PluginA.filter_action -> (None) -> X
  2. X <- (None) <- PluginB.filter_event <- (event e) <- World Server
  3. action a : original update instruction
  4. event e : original update notification
  5. X : processing exit

update can generate update instructions.
update is executed at the interval set byWORLD_TICK_TIME.

ExtraCommandPlugin

This plugin enables the use of the command.
Commands allow you to operate the server.
The plugin return a command processor.
When the client executes the command, the method of the command processor is executed.

For example, ActionCommandProcessor in plugin/action/main.py is below.

  1. from binascii import unhexlify
  2. from pickle import loads
  3. from pyminehub.mcpe.command.annotation import String
  4. from pyminehub.mcpe.command.api import CommandContext, command
  5. class ActionCommandProcessor:
  6. @command
  7. def perform(self, context: CommandContext, args: str) -> None:
  8. """Perform action in the world."""
  9. self._perform(context, String(args))
  10. @perform.overload
  11. def _perform(self, context: CommandContext, data: String) -> None:
  12. action = loads(unhexlify(data))
  13. context.perform_action(action)

ActionCommandProcessor provide /perform data command.
The command name perform is named from method name perform.
By applying the command decorator to the method, the method name becomes the command name.
The method parameters must be CommandContext and str.

Use the overload decorator to provide the method signature to the client.
Signatures are used to indicate usage of the command.
The parameters of the method with overload decorator must be CommandContext and
the types defined in pyminehub.mcpe.command.annotation.
When calling the method, wrapping in the types in pyminehub.mcpe.command.annotation will enable overloading.
For that reason, The method names may be the same.

Client

Not only servers, we also have a simple client.

For example, ActionCommandClient in plugin/action/client.py is below.

  1. from binascii import hexlify
  2. from pickle import dumps
  3. from typing import Union
  4. from pyminehub.mcpe.action import Action, ActionType, action_factory
  5. from pyminehub.mcpe.network import MCPEClient
  6. from pyminehub.mcpe.geometry import Vector3
  7. from pyminehub.mcpe.const import MoveMode
  8. from pyminehub.mcpe.main.client import connect
  9. class ActionCommandMixin:
  10. @property
  11. def client(self) -> MCPEClient:
  12. raise NotImplementedError()
  13. def perform_action(self, action_or_type: Union[Action, ActionType], *args, **kwargs) -> None:
  14. action = action_factory.create(action_or_type, *args, **kwargs) \
  15. if isinstance(action_or_type, ActionType) else action_or_type
  16. data = hexlify(dumps(action)).decode()
  17. self.client.execute_command('/perform {}'.format(data))
  18. class ActionCommandClient(MCPEClient, ActionCommandMixin):
  19. @property
  20. def client(self) -> MCPEClient:
  21. return self
  22. def run_client():
  23. with connect('127.0.0.1', player_name='Taro', client_factory=ActionCommandClient) as client:
  24. client.perform_action(
  25. ActionType.MOVE_PLAYER, 1, Vector3(256.0, 64.0, 256.0), 0.0, 0.0, 0.0, MoveMode.NORMAL, True, 0, True)
  26. client.wait_response(timeout=10)
  27. print(client.get_entity())

It connects to the server by method connect.
To extend the client, specify the parameter client_factory.
For the parameter client_factory, specify a function that creates an instance of a class that extends MCPEClient.
When the connection to the server is successful,
the generated instance is bound to the variable client specified by as.

The method wait_response waits for something packet.
It does not wait for the response of the packet sent immediately before.
If some packet is received, proceed to the next.

Please see test/multiplay.py, too.

Server Config

If you want to change the setting, edit src/pyminehub/mcpe/main/server.py.
For example, the part of src/pyminehub/mcpe/main/server.py is below.

  1. if __name__ == '__main__':
  2. from pyminehub.config import set_config
  3. set_config(game_mode='CREATIVE', spawn_mob=False)
  4. set_config(
  5. world_name='Small',
  6. init_space=(1, 1),
  7. player_spawn_position=(0, 56, 0)
  8. )
  9. # ... snip ...

Calls to set_config can be combined in one operation or divided.
The parameter names is defined in pyminehub.config.

Reference