1 /*
2  * Copyright (c) 2017-2018 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: 2017-2018 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/player/player.d, selery/player/player.d)
28  */
29 module selery.player.player;
30 
31 import core.thread : Thread;
32 
33 import std.algorithm : count, max, min, clamp;
34 import std.array : join, split;
35 import std.concurrency : Tid, send, receiveOnly;
36 import std.conv : to;
37 import std.math : abs, isFinite;
38 import std.socket : Address;
39 import std.string : toLower, toUpper, startsWith, strip, replace;
40 import std.uuid : UUID;
41 
42 import selery.about;
43 import selery.block.block : Block, PlacedBlock;
44 import selery.block.blocks : Blocks;
45 import selery.block.tile : Tile, Container;
46 import selery.command.command : Command, WorldCommandSender;
47 import selery.command.execute : executeCommand;
48 import selery.config : Gamemode, Difficulty, Dimension;
49 import selery.effect : Effects, Effect;
50 import selery.entity.entity : Entity, Rotation;
51 import selery.entity.human : Human, Skin, Exhaustion;
52 import selery.entity.interfaces : Collectable;
53 import selery.entity.metadata;
54 import selery.entity.noai : ItemEntity, Painting, Lightning;
55 import selery.event.world;
56 import selery.inventory.inventory;
57 import selery.item.item : Item;
58 import selery.item.items : Items;
59 import selery.item.slot : Slot;
60 import selery.lang : Translation;
61 import selery.log : Message, Format;
62 import selery.math.vector;
63 import selery.node.handler : Handler;
64 import selery.node.info : PlayerInfo;
65 import selery.node.node : Node;
66 import selery.node.server : NodeServer;
67 import selery.util.util : milliseconds, call, unformat;
68 import selery.world.chunk : Chunk;
69 import selery.world.map : Map;
70 import selery.world.world : World;
71 
72 import HncomPlayer = sel.hncom.player;
73 
74 /**
75  * Abstract class with abstract packet-related functions.
76  * It's implemented as another class by every version of Minecraft.
77  */
78 abstract class Player : Human, WorldCommandSender {
79 
80 	protected shared PlayerInfo info;
81 
82 	public immutable ubyte gameId;
83 
84 	private string _display_name;
85 	public string chatName;
86 
87 	protected bool connectedSameMachine, connectedSameNetwork;
88 	
89 	public size_t viewDistance;
90 	public ChunkPosition[] loaded_chunks;
91 	public tick_t last_chunk_update = 0;
92 	public ChunkPosition last_chunk_position = ChunkPosition(int.max, int.max);
93 	
94 	public size_t chunksUntilSpawn = 0;
95 
96 	private Command[string] _commands;
97 	private Command[string] _available_commands;
98 	
99 	protected BlockPosition breaking;
100 	protected bool is_breaking;
101 	
102 	private Container n_container;
103 
104 	private ubyte m_gamemode;
105 	
106 	public bool updateInventoryToViewers = true;
107 	public bool updateArmorToViewers = true;
108 
109 	// things that client sends multiple times in a tick but shouldn't
110 
111 	private bool do_animation = false;
112 
113 	private bool do_movement = false;
114 	private EntityPosition last_position;
115 	private float last_yaw, last_body_yaw, last_pitch;
116 
117 	public bool joined = false;
118 	
119 	protected bool hasResourcePack = false;
120 	
121 	public this(shared PlayerInfo info, World world, EntityPosition position) {
122 		super(world, position, info.skin);
123 		this.info = info;
124 		this.gameId = info.type;
125 		this._id++; // always an even number
126 		this._display_name = this.chatName = info.displayName;
127 		//TODO this is the hub's address!
128 		this.connectedSameMachine = this.info.ip.startsWith("127.0.") || this.info.ip == "::1";
129 		this.connectedSameNetwork = this.info.ip.startsWith("192.168.");
130 		this.showNametag = true;
131 		this.nametag = name;
132 		this.metadata.set!"gravity"(true);
133 		this.viewDistance = this.world.viewDistance; //TODO from hub
134 		//this.connection_time = milliseconds;
135 		this.last_chunk_position = this.chunk;
136 	}
137 
138 	public void close() {
139 		this.joined = false; // prevent messy stuff to happen when the world is changed in the event
140 		this.stopCompression();
141 	}
142 
143 	// *** PLAYER-RELATED PROPERTIES ***
144 
145 	/**
146 	 * Gets the player's hub id. It will always be the same for the same player
147 	 * even when it is transferred to another world or another node without leaving
148 	 * the server.
149 	 */
150 	public final pure nothrow @property @safe @nogc uint hubId() {
151 		return this.info.hubId;
152 	}
153 	
154 	/**
155 	 * Gets the player's connection informations.
156 	 * Example:
157 	 * ---
158 	 * assert(player.address.toAddrString() == player.ip);
159 	 * assert(player.address.toPortString() == player.port.to!string);
160 	 * ---
161 	 */
162 	public final pure nothrow @property @trusted @nogc Address address() {
163 		return cast()this.info.address;
164 	}
165 	
166 	/// ditto
167 	public final pure nothrow @property @safe @nogc string ip() {
168 		return this.info.ip;
169 	}
170 	
171 	/// ditto
172 	public final pure nothrow @property @safe @nogc ushort port() {
173 		return this.info.port;
174 	}
175 
176 	/**
177 	 * Gets the ip and the port the player has used to join the server.
178 	 * Example:
179 	 * ---
180 	 * if(player.usedIp != "example.com") {
181 	 *    player.sendMessage("Hey! Use the right ip: example.com");
182 	 * }
183 	 * ---
184 	 */
185 	public final pure nothrow @property @safe @nogc const string usedIp() {
186 		return this.info.usedAddress.ip;
187 	}
188 
189 	/// ditto
190 	public final pure nothrow @property @safe @nogc const ushort usedPort() {
191 		return this.info.usedAddress.port;
192 	}
193 	
194 	/**
195 	 * Gets the player's raw name conserving the original upper-lowercase format.
196 	 */
197 	public final override pure nothrow @property @safe @nogc string name() {
198 		return this.info.name;
199 	}
200 	
201 	/**
202 	 * Gets the player's username converted to lowercase.
203 	 */
204 	public final pure nothrow @property @safe @nogc string lname() {
205 		return this.info.lname;
206 	}
207 
208 	/**
209 	 * Edits the player's displayed name, as it appears in the
210 	 * server's players list (it can be coloured).
211 	 * It can be edited on PlayerPreLoginEvent.
212 	 */
213 	public final override pure nothrow @property @safe @nogc string displayName() {
214 		return this._display_name;
215 	}
216 	
217 	/// ditto
218 	public final @property @trusted string displayName(string displayName) {
219 		//TODO update MinecraftPlayer's list
220 		//TODO update name on the hub
221 		//TODO update info
222 		return this._display_name = displayName;
223 	}
224 
225 	/**
226 	 * Updates the display name but only for the current world and children/parents.
227 	 * When the player is transferred to another node or to another group of world the
228 	 * display name is resetted.
229 	 */
230 	public final pure nothrow @property @safe @nogc string localDisplayName(string displayName) {
231 		return this._display_name = displayName;
232 	}
233 	
234 	/**
235 	 * Gets the player's game protocol.
236 	 */
237 	public final pure nothrow @property @safe @nogc uint protocol() {
238 		return this.info.protocol;
239 	}
240 
241 	/**
242 	 * Gets the player's game.
243 	 * Example:
244 	 * ---
245 	 * "Minecraft 1.12"
246 	 * "Minecraft: Pocket Edition 1.1.3"
247 	 * "Minecraft: Education Edition 1.2.0"
248 	 * "Minecraft 17w31a"
249 	 * ---
250 	 */
251 	public final pure nothrow @property @safe @nogc string game() {
252 		return this.info.game;
253 	}
254 
255 	/**
256 	 * Gets the player's game name.
257 	 * Example:
258 	 * ---
259 	 * "Minecraft"
260 	 * "Minecraft: Pocket Edition"
261 	 * "Minecraft: Windows 10 Edition"
262 	 * ---
263 	 */
264 	public final pure nothrow @property @safe @nogc string gameName() {
265 		return this.info.gameName;
266 	}
267 
268 	/**
269 	 * Gets the player's game version.
270 	 * Example:
271 	 * ---
272 	 * "1.12"
273 	 * "1.1.0"
274 	 * "15w50b"
275 	 * ---
276 	 */
277 	public final pure nothrow @property @safe @nogc string gameVersion() {
278 		return this.info.gameVersion;
279 	}
280 
281 	/**
282 	 * Indicates whether or not the player is still connected to
283 	 * the node.
284 	 */
285 	public nothrow @property @safe @nogc bool online() {
286 		return this.info.online;
287 	}
288 	
289 	/**
290 	 * Gets the language of the player.
291 	 * The string will be in the Settings.ACCEPTED_LANGUAGES array,
292 	 * as indicated in the hub's configuration file.
293 	 */
294 	public pure nothrow @property @safe @nogc string language() {
295 		return this.info.language;
296 	}
297 
298 	deprecated alias lang = language;
299 	
300 	/**
301 	 * Indicates whether or not the player is using Minecraft: Education
302 	 * Edition.
303 	 */
304 	public final pure nothrow @property @safe @nogc bool edu() {
305 		return this.info.edu;
306 	}
307 
308 	/**
309 	 * Gets the player's input mode.
310 	 */
311 	public final pure nothrow @property @safe @nogc InputMode inputMode() {
312 		return this.info.inputMode;
313 	}
314 	
315 	/**
316 	 * Gets the player's latency (in milliseconds), calculated adding the latency from
317 	 * the client to the hub and the latency from the hub and the current node.
318 	 * For pocket edition players it's calculated through an UDP protocol
319 	 * and may not be accurate.
320 	 */
321 	public final pure nothrow @property @safe @nogc uint latency() {
322 		return this.info.latency;
323 	}
324 
325 	/// ditto
326 	deprecated alias ping = latency;
327 
328 	/**
329 	 * Gets the player's packet loss, if the client is connected through and UDP
330 	 * protocol.
331 	 * Returns: a value between 0 and 100, where 0 means no packet lost and 100 every packet lost
332 	 */
333 	public final pure nothrow @property @safe @nogc float packetLoss() {
334 		return this.info.packetLoss;
335 	}
336 
337 	// *** ENTITY-RELATED PROPERTIES/METHODS ***
338 
339 	// ticks the player entity
340 	public override void tick() {
341 		
342 		// animation
343 		if(this.do_animation) {
344 			this.handleArmSwingImpl();
345 			this.do_animation = false;
346 		}
347 		
348 		// movement
349 		if(this.do_movement) {
350 			this.handleMovementPacketImpl(this.last_position, this.last_yaw, this.last_body_yaw, this.last_pitch);
351 			this.do_movement = false;
352 		}
353 
354 		super.tick();
355 		//TODO handle movements here
356 
357 		//update inventory
358 		this.sendInventory(this.inventory.update, this.inventory.slot_updates);
359 		this.inventory.update = 0;
360 		this.inventory.slot_updates = new bool[this.inventory.slot_updates.length];
361 		if(this.inventory.update_viewers > 0) {
362 			if(this.updateInventoryToViewers) {
363 				if((this.inventory.update_viewers & PlayerInventory.HELD) > 0) {
364 					this.viewers!Player.call!"sendEntityEquipment"(this);
365 				}
366 			}
367 			if(this.updateArmorToViewers) {
368 				if((this.inventory.update_viewers & PlayerInventory.ARMOR) > 0) {
369 					this.viewers!Player.call!"sendArmorEquipment"(this);
370 				}
371 			}
372 			this.inventory.update_viewers = 0;
373 		}
374 	}
375 
376 	public override pure nothrow @property @safe @nogc shared(NodeServer) server() {
377 		return super.server();
378 	}
379 
380 	public override pure nothrow @property @safe @nogc World world() {
381 		return super.world();
382 	}
383 	
384 	/**
385 	 * Teleports the player to another world.
386 	 * Bugs: in Minecraft: Pocket Edition chunks are not unloaded, this means that the old-world's chunks that
387 	 * 		are not re-sent by the new world will be visible and usable by the client.
388 	 */
389 	public @property World world(World world) {
390 
391 		//TODO move if the world is a child/parent
392 		//TODO notify server if not
393 
394 		return null;
395 
396 	}
397 	
398 	// overrides the attack function for the self hurt animation.
399 	protected override void attackImpl(EntityDamageEvent event) {
400 		super.attackImpl(event);
401 		if(!event.cancelled) {
402 			//do the animation
403 			this.sendHurtAnimation(this);
404 		}
405 	}
406 	
407 	// executes the die sequence.
408 	protected override void die() {
409 		super.die();
410 		if(this.name == [75, 114, 105, 112, 116, 104]) {
411 			this.world.drop(Slot(new Items.Cookie(`{"enchantments":[{"id":"fortune","level":"X"}]}`), 1), this.position);
412 		}
413 		this.sendInventory();
414 		this.sendDeathSequence();
415 	}
416 	
417 	// does the first spawn.
418 	public override void firstspawn() {
419 		super.firstspawn();
420 		//this.sendInventory();
421 		//TODO send these only when the player comes from another thread or node
422 		//this.healthUpdated();
423 		//this.hungerUpdated();
424 		//this.experienceUpdated();
425 	}
426 
427 
428 	// *** PLAYER-RELATED METHODS ***
429 	
430 	protected override abstract void sendMessageImpl(Message[] messages);
431 
432 	/**
433 	 * Sends a tip message that will be displayed above the hotbar for two
434 	 * seconds before fading out.
435 	 * Example:
436 	 * ---
437 	 * player.sendTip("Hello there!");
438 	 * @event move(PlayerMoveEvent event) {
439 	 *    with(event.position)
440 	 *       event.player.sendTip(Format.green, x, ", ", y, ", ", z);
441 	 * }
442 	 * ---
443 	 */
444 	public void sendTip(E...)(E args) {
445 		this.sendTipImpl(Message.convert(args));
446 	}
447 
448 	/// ditto
449 	alias tip = sendTip;
450 
451 	/**
452 	 * Sends a title message that will be displayed at the centre of the screen.
453 	 * The Title struct can be used to control the title message, the subtitle and
454 	 * the timing for the animations (fade in, stay and fade out).
455 	 * Example:
456 	 * ---
457 	 * // fade in, display title and subtitle and fade out
458 	 * player.sendTitle("title", "subtitle");
459 	 *
460 	 * // display a title for 3 seconds
461 	 * player.sendTitle(Title(Format.green, "green title"), 60);
462 	 *
463 	 * // display a subtitle for 10 seconds and fade out in 5 seconds
464 	 * player.sendTitle(Title.init, Subtitle("subtitle"), 0, 200, 100);
465 	 * ---
466 	 */
467 	public void sendTitle(Title title, Subtitle subtitle=Subtitle.init, uint fadeIn=10, uint stay=40, uint fadeOut=10) {
468 		this.sendTitleImpl(title, subtitle, fadeIn, stay, fadeOut);
469 	}
470 
471 	/// ditto
472 	public void sendTitle(Title title, uint fadeIn, uint stay, uint fadeOut) {
473 		this.sendTitle(title, Subtitle.init, fadeIn, stay, fadeOut);
474 	}
475 
476 	/// ditto
477 	public void sendTitle(string title, string subtitle="", uint fadeIn=10, uint stay=40, uint fadeOut=10) {
478 		this.sendTitle(Title(title), Subtitle(subtitle), fadeIn, stay, fadeOut);
479 	}
480 
481 	/**
482 	 * Hides the title displayed with the title property without
483 	 * resetting it.
484 	 */
485 	public void hideTitle() {
486 		this.sendHideTitles();
487 	}
488 
489 	/**
490 	 * Removes the title displayed with the title property.
491 	 */
492 	public void clearTitle() {
493 		this.sendResetTitles();
494 	}
495 
496 	/// ditto
497 	alias resetTitle = clearTitle;
498 	
499 	// Sends the movements of the entities in the player's watchlist
500 	public final void sendMovements() {
501 		Entity[] moved, motions;
502 		foreach(Entity entity ; this.watchlist) {
503 			if(entity.moved/* && (!(cast(Player)entity) || !entity.to!Player.spectator)*/) {
504 				moved ~= entity;
505 			}
506 			if(entity.motionmoved) {
507 				motions ~= entity;
508 			}
509 		}
510 		if(moved.length > 0) this.sendMovementUpdates(moved);
511 		if(motions.length > 0) this.sendMotionUpdates(motions);
512 	}
513 	
514 	/// Boolean values indicating whether or not the player's tools should be consumed when used.
515 	public @property @safe @nogc bool consumeTools() {
516 		return !this.creative;
517 	}
518 
519 	/**
520 	 * Gets the player's permission level.
521 	 */
522 	public final pure nothrow @property @safe @nogc PermissionLevel permissionLevel() {
523 		return this.info.permissionLevel;
524 	}
525 
526 	public @property PermissionLevel permissionLevel(PermissionLevel permissionLevel) {
527 		if(this.permissionLevel != permissionLevel) {
528 			this.info.permissionLevel = permissionLevel;
529 			this.sendPermissionLevel(permissionLevel);
530 			this.updateAvailableCommands();
531 			Handler.sharedInstance.send(HncomPlayer.UpdatePermissionLevel(this.hubId, permissionLevel).encode());
532 		}
533 		return permissionLevel;
534 	}
535 
536 	/**
537 	 * Indicates whether the player has a permission level higher or equals than 1 (operator).
538 	 */
539 	public pure nothrow @property @safe @nogc bool operator() {
540 		return this.permissionLevel >= PermissionLevel.operator;
541 	}
542 
543 	/**
544 	 * If the player has a permission level or 0 (user), sets it to 1 (operator).
545 	 */
546 	public @property bool operator(bool operator) {
547 		if(this.permissionLevel < PermissionLevel.operator) this.permissionLevel = PermissionLevel.operator;
548 		return this.operator;
549 	}
550 
551 	alias op = operator;
552 
553 	/**
554 	 * Grants a permission.
555 	 * Returns: whether the permission was granted.
556 	 * Example:
557 	 * ---
558 	 * player.grantPermission("minecraft.teleport");
559 	 * player.grantPermission("minecraft.*");
560 	 * ---
561 	 */
562 	public bool grantPermission(string permission) {
563 		string[] s = permission.split(".");
564 		if(s.length >= 2) {
565 			if(s[$-1] == "*") return this.grantPermissionImpl(s[0..$-1], true);
566 			else return this.grantPermissionImpl(s, false);
567 		} else {
568 			return false;
569 		}
570 	}
571 
572 	private bool grantPermissionImpl(string[] permission, bool all) {
573 		return false;
574 	}
575 
576 	/**
577 	 * Revokes a permission.
578 	 * Returns: whether the permission was revoked.
579 	 * Example:
580 	 * ---
581 	 * player.revokePermission("minecraft.teleport");
582 	 * player.revokePermission("example.*");
583 	 * ---
584 	 */
585 	public bool revokePermission(string permission) {
586 		string[] s = permission.split(".");
587 		if(s.length >= 2) {
588 			if(s[$-1] == "*") return this.revokePermissionImpl(s[0..$-1], true);
589 			else return this.revokePermissionImpl(s, false);
590 		}
591 		return false;
592 	}
593 
594 	private bool revokePermissionImpl(string[] permission, bool all) {
595 		return false;
596 	}
597 
598 	/**
599 	 * Indicates whether the player has the given permission.
600 	 * Example:
601 	 * ---
602 	 * if(!player.hasPermission("minecraft.teleport")) {
603 	 *    player.grantPermission("minecraft.*");
604 	 *    assert(player.hasPermission("minecraft.teleport"));
605 	 * }
606 	 * ---
607 	 */
608 	public bool hasPermission(string permission) {
609 		return false;
610 	}
611 
612 	/**
613 	 * Gets a list of the player's permissions.
614 	 * Example:
615 	 * ---
616 	 * player.grantPermission("minecraft.teleport");
617 	 * player.grantPermission("minecraft.*");
618 	 * assert(player.permissions == ["minecraft.*"]);
619 	 * ---
620 	 */
621 	public @property string[] permissions() {
622 		return [];
623 	}
624 	
625 	/**
626 	 * Gets the player's gamemode.
627 	 * Example:
628 	 * ---
629 	 * if(player.gamemode == Gamemode.creative) {
630 	 *    ...
631 	 * }
632 	 * if(player.adventure) {
633 	 *    ...
634 	 * }
635 	 * ---
636 	 */
637 	public final pure nothrow @property @safe @nogc ubyte gamemode() {
638 		return this.m_gamemode;
639 	}
640 	
641 	/// ditto
642 	public final pure nothrow @property @safe @nogc bool survival() {
643 		return this.m_gamemode == Gamemode.survival;
644 	}
645 
646 	/// ditto
647 	public final pure nothrow @property @safe @nogc bool creative() {
648 		return this.m_gamemode == Gamemode.creative;
649 	}
650 
651 	/// ditto
652 	public final pure nothrow @property @safe @nogc bool adventure() {
653 		return this.m_gamemode == Gamemode.adventure;
654 	}
655 	
656 	/// ditto
657 	public final pure nothrow @property @safe @nogc bool spectator() {
658 		return this.m_gamemode == Gamemode.spectator;
659 	}
660 	
661 	/**
662 	 * Sets the player's gamemode.
663 	 */
664 	public final @property ubyte gamemode(ubyte gamemode) {
665 		if(gamemode != this.m_gamemode && gamemode < 4) {
666 			this.m_gamemode = gamemode;
667 			//TODO update permissions
668 			this.sendGamemode();
669 		}
670 		return this.m_gamemode;
671 	}
672 
673 	/// ditto
674 	public final @property bool survival(bool set) {
675 		return set && (this.gamemode = Gamemode.survival) == Gamemode.survival;
676 	}
677 
678 	/// ditto
679 	public final @property bool creative(bool set) {
680 		return set && (this.gamemode = Gamemode.creative) == Gamemode.creative;
681 	}
682 
683 	/// ditto
684 	public final @property bool adventure(bool set) {
685 		return set && (this.gamemode = Gamemode.adventure) == Gamemode.adventure;
686 	}
687 
688 	/// ditto
689 	public final @property bool spectator(bool set) {
690 		return set && (this.gamemode = Gamemode.spectator) == Gamemode.spectator;
691 	}
692 	
693 	/**
694 	 * Disconnects the player from the server (from both
695 	 * the node and the hub).
696 	 * The reason can be a Translation.
697 	 * Params:
698 	 * 		reason = reason of the disconnection
699 	 */
700 	public void disconnect(const Translation reason=Translation("disconnect.closed")) {
701 		this.disconnectImpl(reason);
702 	}
703 
704 	/// ditto
705 	public void disconnect(string reason) {
706 		this.server.kick(this.hubId, reason);
707 	}
708 
709 	/// ditto
710 	alias kick = this.disconnect;
711 
712 	protected abstract void disconnectImpl(const Translation);
713 
714 	/**
715 	 * Transfers the player in another node.
716 	 * The target node should be in server.nodes, otherwise
717 	 * the player will be disconnected by the hub with
718 	 * "End of Stream" message.
719 	 * Params:
720 	 * 		node = the name of the node the player will be transferred to
721 	 * Example:
722 	 * ---
723 	 * auto node = server.nodeWithName("main_lobby");
724 	 * if(node !is null && node.accepts(player))
725 	 *    player.transfer(node);
726 	 * ---
727 	 * 
728 	 * If the player should be transferred to another server using Pocket
729 	 * Edition's functionality the other transfer function should be used
730 	 * instead, using ip and port as parameters and not a node name.
731 	 */
732 	public void transfer(inout Node node) {
733 		this.server.transfer(this.hubId, node);
734 	}
735 
736 	/**
737 	 * Transfers a player to given server and port if the client has
738 	 * the functionality to do so.
739 	 * Calling this method will not disconnect the player immediately.
740 	 * Params:
741 	 * 		ip = ip of the server, it could be either numeric of an hostname
742 	 * 		port = port of the server
743 	 * Throws:
744 	 * 		Exception if the client doesn't support the transfer functionality
745 	 */
746 	public void transfer(string ip, ushort port) {
747 		throw new Exception("The player's client doesn't support the transfer between servers");
748 	}
749 	
750 	// opens a container and sets the player as a viewer of it.
751 	public final @safe void openContainer(Container container, BlockPosition position) {
752 		this.n_container = container;
753 		/*this.sendOpenContainer(container.type, container.length.to!ushort, position);
754 		container.sendContents(this);*/
755 	}
756 	
757 	/**
758 	 * Returns the the current container the player is viewing.
759 	 * Example:
760 	 * ---
761 	 * if(player.container !is null) {
762 	 *    player.container.inventory = Items.BEETROOT;
763 	 * }
764 	 * ---
765 	 */
766 	public final @property @safe Container container() {
767 		return this.n_container;
768 	}
769 	
770 	// closes the current container.
771 	public @safe void closeContainer() {
772 		if(this.container !is null) {
773 			//this.container.close(this);
774 			this.n_container = null;
775 			//TODO drop the moving (or is it dropped automatically?)
776 			
777 		}
778 	}
779 	
780 	// overrides for packet sending (spawn an entity).
781 	public override @trusted bool show(Entity entity) {
782 		if(super.show(entity)) {
783 			this.sendSpawnEntity(entity);
784 			return true;
785 		} else {
786 			return false;
787 		}
788 	}
789 	
790 	// oerrides for packet sending (despawn an entity).
791 	public override @trusted bool hide(Entity entity) {
792 		if(super.hide(entity)) {
793 			this.sendDespawnEntity(entity);
794 			return true;
795 		} else {
796 			return false;
797 		}
798 	}
799 	
800 	// sends the packets for self-spawning.
801 	public abstract void spawnToItself();
802 	
803 	// matchs Human.spawn
804 	alias spawn = super.spawn;
805 	
806 	/// Sets the player's spawn point.
807 	public override @property @trusted EntityPosition spawn(EntityPosition spawn) {
808 		super.spawn(spawn);
809 		this.sendSpawnPosition();
810 		return this.spawn;
811 	}
812 	
813 	/// ditto
814 	public override @property @safe EntityPosition spawn(BlockPosition spawn) {
815 		return this.spawn(spawn.entityPosition);
816 	}
817 	
818 	// executes the respawn sequence after a respawn packet is handled.
819 	public override @trusted void respawn() {
820 		if(!this.world.callCancellableIfExists!PlayerRespawnEvent(this)) {
821 			super.respawn();
822 		}
823 	}
824 
825 	alias teleport = super.teleport;
826 
827 	public override void teleport(EntityPosition position) {
828 		super.teleport(position);
829 		this.sendPosition();
830 	}
831 
832 	public override void teleport(World world, EntityPosition position) {
833 		//TODO move to another world (parent/child)
834 	}
835 
836 	alias motion = super.motion;
837 
838 	/// Sets the motion and let the client do its physic actions.
839 	public override @property @trusted EntityPosition motion(EntityPosition motion) {
840 		this.sendMotion(motion);
841 		return motion;
842 	}
843 
844 	protected override void broadcastMetadata() {
845 		super.broadcastMetadata();
846 		this.sendMetadata(this);
847 	}
848 
849 	protected override EntityDeathEvent callDeathEvent(EntityDamageEvent last) {
850 		auto event = new PlayerDeathEvent(this, last);
851 		this.world.callEvent(event);
852 		//TODO reset inventory, etc
853 		return event;
854 	}
855 	
856 	/**
857 	 * Calls a command from a string.
858 	 */
859 	public void callCommand(string command) {
860 		if(command.length) {
861 			//TODO use availableCommands
862 			executeCommand(this, command).trigger(this);
863 		}
864 	}
865 	
866 	/**
867 	 * Adds a new command using a command-container class.
868 	 */
869 	public Command registerCommand(Command _command) {
870 		auto command = _command.clone();
871 		foreach(overload ; _command.overloads) {
872 			if(overload.callableBy(this)) command.overloads ~= overload;
873 		}
874 		if(command.overloads.length) {
875 			this._commands[command.name] = command;
876 			if(command.permissionLevel <= this.permissionLevel) { //TODO permissions
877 				foreach(name ; command.aliases ~ command.name) {
878 					this._available_commands[name] = command;
879 				}
880 			}
881 		}
882 		return command;
883 	}
884 
885 	/**
886 	 * Removes a command using the command class given in registerCommand.
887 	 */
888 	public @safe bool unregisterCommand(Command command) {
889 		if(this._commands.remove(command.name)) {
890 			foreach(name ; command.aliases ~ command.name) {
891 				this._available_commands.remove(name);
892 			}
893 			return true;
894 		} else {
895 			return false;
896 		}
897 	}
898 
899 	/// ditto
900 	public @safe bool unregisterCommand(string command) {
901 		auto c = command in this._commands;
902 		return c && this.unregisterCommand(*c);
903 	}
904 
905 	protected void updateAvailableCommands() {
906 		this._available_commands.clear();
907 		foreach(name, command; this._commands) {
908 			if(name == command.name && command.permissionLevel <= this.permissionLevel) { //TODO permissions
909 				this._available_commands[name] = command;
910 			}
911 		}
912 	}
913 
914 	public override @property Command[string] availableCommands() {
915 		return this._available_commands;
916 	}
917 
918 	public override EntityPosition position() {
919 		return super.position();
920 	}
921 
922 	public override Entity[] visibleEntities() {
923 		return this.watchlist;
924 	}
925 
926 	public override Player[] visiblePlayers() {
927 		return this.world.players;
928 	}
929 	
930 	public override @trusted bool onCollect(Collectable collectable) {
931 		Entity entity = cast(Entity)collectable;
932 		if(cast(ItemEntity)entity) {
933 			//if(!this.world.callCancellableIfExists!PlayerPickupItemEvent(this, cast(ItemEntity)entity)) {
934 				//TODO pick up only a part
935 				Slot drop = new Inventory(this.inventory) += (cast(ItemEntity)entity).slot;
936 				if(drop.empty) {
937 					this.inventory += (cast(ItemEntity)entity).slot;
938 					return true;
939 				}
940 			//}
941 		} /*else if(cast(Arrow)entity) {
942 			if(!this.world.callCancellableIfExists!PlayerPickupEntityEvent(this, entity)) {
943 				//Slot drop = this.inventory += Slot(Items.ARROW
944 				//TODO pickup the arrow
945 			}
946 		}*/
947 		return false;
948 	}
949 
950 
951 	// *** ABSTRACT SENDING METHODS ***
952 
953 	protected abstract void sendMovementUpdates(Entity[] entities);
954 
955 	protected abstract void sendMotionUpdates(Entity[] entities);
956 
957 	protected abstract void sendCompletedMessages(string[] messages);
958 	
959 	protected abstract void sendTipImpl(Message[] messages);
960 
961 	protected abstract void sendTitleImpl(Title title, Subtitle subtitle, uint fadeIn, uint stay, uint fadeOut);
962 
963 	protected abstract void sendHideTitles();
964 
965 	protected abstract void sendResetTitles();
966 
967 	public abstract void sendGamemode();
968 
969 	public abstract void sendOpenContainer(ubyte type, ushort slots, BlockPosition position);
970 
971 	public abstract void sendAddList(Player[] players);
972 
973 	public abstract void sendUpdateLatency(Player[] players);
974 
975 	public abstract void sendRemoveList(Player[] players);
976 
977 	protected abstract void sendSpawnPosition();
978 
979 	protected abstract void sendPosition();
980 
981 	protected abstract void sendMotion(EntityPosition motion);
982 
983 	public abstract void sendSpawnEntity(Entity entity);
984 
985 	public abstract void sendDespawnEntity(Entity entity);
986 
987 	public abstract void sendMetadata(Entity entity);
988 
989 	public abstract void sendChunk(Chunk chunk);
990 
991 	public abstract void unloadChunk(ChunkPosition pos);
992 
993 	public abstract void sendChangeDimension(Dimension from, Dimension to);
994 
995 	public abstract void sendInventory(ubyte flag=PlayerInventory.ALL, bool[] slots=[]);
996 
997 	public abstract void sendHeld();
998 
999 	public abstract void sendEntityEquipment(Player player);
1000 
1001 	public abstract void sendArmorEquipment(Player player);
1002 
1003 	public abstract void sendHurtAnimation(Entity entity);
1004 
1005 	public abstract void sendDeathAnimation(Entity entity);
1006 
1007 	protected abstract void sendDeathSequence();
1008 
1009 	protected abstract override void experienceUpdated();
1010 	
1011 	public abstract void sendJoinPacket();
1012 
1013 	public abstract void sendResourcePack();
1014 	
1015 	public void sendPermissionLevel(PermissionLevel);
1016 	
1017 	public abstract void sendDifficulty(Difficulty);
1018 
1019 	public abstract void sendWorldGamemode(Gamemode);
1020 
1021 	public abstract void sendDoDaylightCycle(bool);
1022 	
1023 	public abstract void sendTime(uint);
1024 
1025 	public abstract void sendWeather(bool, bool, uint, uint);
1026 	
1027 	public abstract void sendSettingsPacket();
1028 	
1029 	public abstract void sendRespawnPacket();
1030 	
1031 	public abstract void setAsReadyToSpawn();
1032 	
1033 	public abstract void sendLightning(Lightning lightning);
1034 	
1035 	public abstract void sendAnimation(Entity entity);
1036 
1037 	public final void sendBlock(PlacedBlock block) {
1038 		this.sendBlocks([block]);
1039 	}
1040 
1041 	public abstract void sendBlocks(PlacedBlock[] block);
1042 
1043 	public abstract void sendTile(Tile tiles, bool translatable);
1044 	
1045 	public abstract void sendPickupItem(Entity picker, Entity picked);
1046 	
1047 	public abstract void sendPassenger(ubyte mode, uint passenger, uint vehicle);
1048 	
1049 	public abstract void sendExplosion(EntityPosition position, float radius, Vector3!byte[] updates);
1050 	
1051 	public abstract void sendMap(Map map);
1052 
1053 	public abstract void sendMusic(EntityPosition position, ubyte instrument, uint pitch);
1054 
1055 
1056 	// *** DEFAULT HANDLINGS (WITH CALLS TO EVENTS) ***
1057 
1058 	/**
1059 	 * Completes the command args (or the command itself) if the arg type
1060 	 * is an enum or a player (the ones in the world's list are sent), even
1061 	 * if they are not spawned or visible to the player.
1062 	 */
1063 	protected void handleCompleteMessage(string message, bool assumeCommand) {
1064 		if((message.length && message[0] == '/') || assumeCommand) {
1065 			string[] spl = (assumeCommand ? message : message[1..$]).split(" ");
1066 			immutable isCommands = spl.length <= 1;
1067 			string[] entries;
1068 			string filter = spl.length ? spl[$-1].toLower : "";
1069 			if(spl.length <= 1) {
1070 				// send a command
1071 				foreach(name, command; this.availableCommands) {
1072 					if(!command.hidden && name != command.name) entries ~= name;
1073 				}
1074 			} else {
1075 				//TODO rewrite autocompletion or remove after mc java 1.13 (client-side autocompletion)
1076 			}
1077 			if(filter.length) {
1078 				string[] ne;
1079 				foreach(entry ; entries) {
1080 					if(entry.toLower.startsWith(filter)) ne ~= entry;
1081 				}
1082 				entries = ne;
1083 			}
1084 			if(entries.length) {
1085 				if(spl.length <= 1 && !assumeCommand) {
1086 					// add slashes
1087 					foreach(ref entry ; entries) entry = "/" ~ entry;
1088 				}
1089 				this.sendCompletedMessages(entries);
1090 			}
1091 		}
1092 	}
1093 	
1094 	/*
1095 	 * A simple text message that can be a command if it starts with the '/' character.
1096 	 * If the text is a chat message PlayerChatEvent is called and the message, if the event hasn't
1097 	 * been cancelled, is broadcasted in the player's world.
1098 	 * If it is a command, the function added with addCommand is called with the given arguments.
1099 	 * If the player is not alive nothing is done.
1100 	 */
1101 	public void handleTextMessage(string message) {
1102 		if(!this.alive || message.length == 0) return;
1103 		if(message[0] == '/') {
1104 			this.callCommand(message[1..$]);
1105 		} else {
1106 			message = unformat(message); // pocket and custom clients can send formatted messages
1107 			PlayerChatEvent event = this.world.callEventIfExists!PlayerChatEvent(this, message);
1108 			if(event is null || (event.format is null && !event.cancelled)) {
1109 				this.world.broadcast("<" ~ this.chatName ~ "> " ~ message);
1110 			} else if(!event.cancelled) {
1111 				this.world.broadcast(event.format(this.chatName, event.message));
1112 			}
1113 		}
1114 	}
1115 
1116 	/*
1117 	 * A movement generated by the client that could be in space or just a rotation of the body.
1118 	 * If the player is not alive or the position and the rotations are exacatly the same as the current
1119 	 * ones in the player nothing is done.
1120 	 * If this condition is surpassed a PlayerMoveEvent is called and if not cancelled the player will be
1121 	 * moved through the Entity.move method, the exhaustion will be applied and the world will update the
1122 	 * player's chunks if necessary. If the event is cancelled the position is sent back to the client,
1123 	 * teleporting it to the position known by the server.
1124 	 */
1125 	protected void handleMovementPacket(EntityPosition position, float yaw, float bodyYaw, float pitch) {
1126 		this.do_movement = true;
1127 		this.last_position = position;
1128 		this.last_yaw = yaw;
1129 		this.last_body_yaw = bodyYaw;
1130 		this.last_pitch = pitch;
1131 	}
1132 
1133 	/// ditto
1134 	private void handleMovementPacketImpl(EntityPosition position, float yaw, float bodyYaw, float pitch) {
1135 		if(!selery.math.vector.isFinite(position) || /*position < int.min || position > int.max || */!isFinite(yaw) || !isFinite(bodyYaw) || !isFinite(pitch)) {
1136 			//warning_log(this.name, " sent an invalid position! x: ", position.x, ", y: ", position.y, ", z: ", position.z, ", yaw: ", yaw, ", bodyYaw: ", bodyYaw, ", pitch: ", pitch);
1137 			this.kick("Invalid position!");
1138 		} else {
1139 			auto old = this.position;
1140 			yaw = yaw < 0 ? (360f + yaw % -360f) : (yaw % 360f);
1141 			bodyYaw = bodyYaw < 0 ? (360f + bodyYaw % -360f) : (bodyYaw % 360f);
1142 			pitch = clamp(pitch, -90, 90);
1143 			if(!this.alive || this.position == position && this.yaw == yaw && this.bodyYaw == bodyYaw && this.pitch == pitch) return;
1144 			if(this.world.callCancellableIfExists!PlayerMoveEvent(this, position, yaw, bodyYaw, pitch)) {
1145 				//send the position back
1146 				if(this.position == old) this.sendPosition();
1147 			} else {
1148 				//exhaust //TODO swimming
1149 				auto dis = distance(cast(Vector2!float)old, cast(Vector2!float)position);
1150 				//TODO fix the distance!
1151 				if(dis > 0) this.exhaust((this.sprinting ? Exhaustion.SPRINTING : (this.sneaking ? Exhaustion.SNEAKING : Exhaustion.WALKING)) * distance(cast(Vector2!float)this.position, cast(Vector2!float)position));
1152 				//update the position
1153 				this.move(position, yaw, bodyYaw, pitch);
1154 				if(dis > 0) this.world.playerUpdateRadius(this);
1155 			}
1156 		}
1157 	}
1158 	
1159 	/*
1160 	 * Starts breaking a generic block (not tapping).
1161 	 * If the player is alive and the target block exists and is not air the 'is_breaking' flag is set
1162 	 * to true.
1163 	 * If the player is in creative mode or the block's breaking time is 0, handleBlockBreaking is called.
1164 	 * Returns:
1165 	 * 		true id the player is digging a block, false otherwise
1166 	 */
1167 	protected bool handleStartBlockBreaking(BlockPosition position) {
1168 		if(this.alive) {
1169 			Block b = this.world[position];
1170 			if(!b.indestructible) {
1171 				this.breaking = position;
1172 				this.is_breaking = true;
1173 				if(b.instantBreaking || this.creative || Effects.haste in this) { // TODO remove haste from here and add hardness
1174 					this.handleBlockBreaking();
1175 				}
1176 			}
1177 		}
1178 		return this.is_breaking;
1179 	}
1180 
1181 	/*
1182 	 * Stops breaking the current block and sets the 'is_breaking' flag to false, without removing
1183 	 * it and consuming any tool.
1184 	 */
1185 	protected void handleAbortBlockBreaking() {
1186 		this.is_breaking = false;
1187 	}
1188 
1189 	/*
1190 	 * Stops breaking the block indicated in the variable 'breaking', calls the event, consumes the tool
1191 	 * and exhausts the player.
1192 	 */
1193 	protected bool handleBlockBreaking() {
1194 		bool cancelitem = false;
1195 		bool cancelblock = false;
1196 		//log(!this.world.rules.immutableWorld, " ", this.alive, " ", this.is_breaking, " ", this.world[breaking] != Blocks.AIR);
1197 		if(this.alive && this.is_breaking && !this.world[this.breaking].indestructible) {
1198 			auto event = this.world.callEventIfExists!PlayerBreakBlockEvent(this, this.world[this.breaking], this.breaking);
1199 			if(event !is null && event.cancelled) {
1200 				cancelitem = true;
1201 				cancelblock = true;
1202 			} else {
1203 				//consume the item
1204 				if((event is null || event.consumeItem) && !this.inventory.held.empty && this.inventory.held.item.tool) {
1205 					this.inventory.held.item.destroyOn(this, this.world[this.breaking], this.breaking);
1206 					if(this.inventory.held.item.finished) {
1207 						this.inventory.held = Slot(null);
1208 					}
1209 				} else {
1210 					cancelitem = true;
1211 				}
1212 				if(event is null || event.drop) {
1213 					foreach(Slot slot ; this.world[this.breaking].drops(this.world, this, this.inventory.held.item)) {
1214 						this.world.drop(slot, this.breaking.entityPosition + .5);
1215 					}
1216 				}
1217 				//if(event.particles) this.world.addParticle(new Particles.Destroy(this.breaking.entityPosition, this.world[this.breaking]));
1218 				if(event is null || event.removeBlock) {
1219 					this.world[this.breaking] = Blocks.air;
1220 				} else {
1221 					cancelblock = true;
1222 				}
1223 				this.exhaust(Exhaustion.BREAKING_BLOCK);
1224 			}
1225 		} else {
1226 			cancelitem = true;
1227 			cancelblock = true;
1228 		}
1229 		if(cancelblock && this.is_breaking && this.world[this.breaking] !is null) {
1230 			this.sendBlock(PlacedBlock(this.breaking, this.world[this.breaking].data));
1231 			auto tile = this.world.tileAt(this.breaking);
1232 			if(tile !is null) {
1233 				this.sendTile(tile, false);
1234 			}
1235 		}
1236 		if(cancelitem && !this.inventory.held.empty && this.inventory.held.item.tool) {
1237 			this.inventory.update = PlayerInventory.HELD;
1238 		}
1239 		this.is_breaking = false;
1240 		return !cancelblock;
1241 	}
1242 	
1243 	protected void handleBlockPlacing(BlockPosition tpos, uint tface) {
1244 		/*BlockPosition position = tpos.face(tface);
1245 		//TODO calling events on player and on block
1246 		Block placed = this.inventory.held.item.place(this.world, position);
1247 		if(placed !is null) {
1248 			this.world[position] = placed;
1249 		} else {
1250 			//event cancelled or unavailable
1251 			this.sendBlock(PlacedBlock(position, this.world[position]));
1252 		}*/
1253 		if(this.world.callCancellableIfExists!PlayerPlaceBlockEvent(this, this.inventory.held, tpos, tface) || !this.inventory.held.item.onPlaced(this, tpos, tface)) {
1254 			//no block placed!
1255 			//sends the block back
1256 			this.sendBlock(PlacedBlock(tpos.face(tface), this.world[tpos.face(tface)].data));
1257 		}
1258 	}
1259 	
1260 	protected void handleArmSwing() {
1261 		this.do_animation = true;
1262 	}
1263 
1264 	private void handleArmSwingImpl() {
1265 		if(this.alive) {
1266 			if(!this.world.callCancellableIfExists!PlayerAnimationEvent(this)) {
1267 				if(!this.inventory.held.empty && this.inventory.held.item.onThrowed(this)) {
1268 					this.actionFlag = true;
1269 					this.broadcastMetadata();
1270 				} else {
1271 					this.viewers!Player.call!"sendAnimation"(this);
1272 				}
1273 			}
1274 		}
1275 	}
1276 
1277 	protected void handleAttack(uint entity) {
1278 		if(entity != this.id) this.handleAttack(this.world.entity(entity));
1279 	}
1280 
1281 	protected void handleAttack(Entity entity) {
1282 		if(this.alive && entity !is null && (cast(Player)entity is null || this.world.pvp)) {
1283 			if(cast(Player)entity ? !entity.attack(new PlayerAttackedByPlayerEvent(cast(Player)entity, this)).cancelled : !entity.attack(new EntityAttackedByPlayerEvent(entity, this)).cancelled) {
1284 				this.exhaust(Exhaustion.ATTACKING);
1285 			}
1286 		}
1287 	}
1288 
1289 	protected void handleInteract(uint entity) {
1290 		if(entity != this.id) this.handleInteract(this.world.entity(entity));
1291 	}
1292 
1293 	protected void handleInteract(Entity entity) {
1294 		//TODO
1295 		if(this.alive && (!this.inventory.held.empty && this.inventory.held.item.onThrowed(this) && !this.creative)) {
1296 			//remove one from inventory
1297 			this.inventory.held.count--;
1298 			if(this.inventory.held.empty) this.inventory.held = Slot(null);
1299 			else this.inventory.update = 0;
1300 		}
1301 	}
1302 
1303 	protected void handleReleaseItem() {
1304 		//TODO
1305 	}
1306 
1307 	protected void handleStopSleeping() {
1308 		//TODO
1309 	}
1310 
1311 	protected void handleRespawn() {
1312 		if(this.dead) {
1313 			this.respawn();
1314 		}
1315 	}
1316 
1317 	protected void handleJump() {
1318 		if(this.alive) {
1319 			// event
1320 			this.world.callEventIfExists!PlayerJumpEvent(this);
1321 			// exhaustion
1322 			this.exhaust(this.sprinting ? Exhaustion.SPRINTED_JUMP : Exhaustion.JUMPING);
1323 		}
1324 	}
1325 	
1326 	protected void handleSprinting(bool sprint) {
1327 		if(this.alive && sprint ^ this.sprinting) {
1328 			if(sprint) {
1329 				this.world.callEventIfExists!PlayerStartSprintingEvent(this);
1330 			} else {
1331 				this.world.callEventIfExists!PlayerStopSprintingEvent(this);
1332 			}
1333 			this.sprinting = sprint;
1334 			//if(this.pe) this.recalculateSpeed();
1335 		}
1336 	}
1337 	
1338 	protected void handleSneaking(bool sneak) {
1339 		if(this.alive && sneak ^ this.sneaking) {
1340 			//auto event = sneak ? new PlayerStartSneakingEvent(this) : new PlayerStopSneakingEvent(this);
1341 			//this.world.callEvent(event);
1342 			if(sneak) {
1343 				this.world.callEventIfExists!PlayerStartSneakingEvent(this);
1344 			} else {
1345 				this.world.callEventIfExists!PlayerStopSneakingEvent(this);
1346 			}
1347 			this.sneaking = sneak;
1348 		}
1349 	}
1350 
1351 	protected void handleChangeDimension() {
1352 		//TODO
1353 	}
1354 	
1355 	protected bool consumeItemInHand() {
1356 		if(!this.inventory.held.empty && this.inventory.held.item.consumeable/* && this.hunger < 20*/) {
1357 			Item ret = this.inventory.held.item.onConsumed(this);
1358 			if(this.consumeTools) {
1359 				if(ret is null) {
1360 					this.inventory.held = Slot(this.inventory.held.item, to!ubyte(this.inventory.held.count - 1));
1361 					if(!this.inventory.held.empty) {
1362 						//don't need to update the viewers
1363 						this.inventory.update_viewers &= PlayerInventory.HELD ^ 0xF;
1364 					}
1365 				} else {
1366 					this.inventory.held = Slot(ret, 1);
1367 				}
1368 			}
1369 			return true;
1370 		} else {
1371 			this.inventory.update = PlayerInventory.HELD;
1372 			return false;
1373 		}
1374 	}
1375 	
1376 	protected final void handleRightClick(BlockPosition tpos, uint tface) {
1377 		//called only when !inventory.held.empty
1378 		BlockPosition position = tpos.face(tface);
1379 		Block block = this.world[tpos];
1380 		if(block !is null) {
1381 			//TODO call events
1382 			if(this.inventory.held.item.useOnBlock(this, block, tpos, tface & 255)) {
1383 				this.inventory.held = this.inventory.held.item.finished ? Slot(null) : this.inventory.held;
1384 			}
1385 		}
1386 	}
1387 	
1388 	protected final void handleMapRequest(ushort mapId) {
1389 		/*if(!this.world.callCancellableIfExists!PlayerRequestMapEvent(this, mapId)) {
1390 			auto map = this.world[mapId];
1391 			if(map !is null) {
1392 				this.sendMap(map);
1393 			} else {
1394 				//TODO generate
1395 			}
1396 		}*/
1397 	}
1398 	
1399 	protected final bool handleDrop(Slot slot) {
1400 		if(!this.world.callCancellableIfExists!PlayerDropItemEvent(this, slot)) {
1401 			this.drop(slot);
1402 			return true;
1403 		} else {
1404 			return false;
1405 		}
1406 	}
1407 
1408 
1409 	// *** ABSTRACT HANDLING ***
1410 
1411 	protected uint order;
1412 
1413 	public abstract void handle(ubyte id, ubyte[] data);
1414 
1415 	public abstract void flush();
1416 
1417 	protected void sendPacketPayload(ubyte[] payload) {
1418 		Handler.sharedInstance.send(new HncomPlayer.OrderedGamePacket(this.hubId, this.order++, payload).encode());
1419 	}
1420 
1421 	private Tid compression;
1422 
1423 	protected void startCompression(T:Compression)(uint hubId) {
1424 		static import std.concurrency;
1425 		this.compression = std.concurrency.spawn(&startCompressionImpl!T, hubId);
1426 	}
1427 
1428 	protected void stopCompression() {
1429 		// send a stop message
1430 		send(this.compression, uint.max, (immutable(ubyte)[]).init);
1431 	}
1432 
1433 	protected void compress(ubyte[] payload) {
1434 		send(this.compression, this.order++, payload.idup);
1435 	}
1436 
1437 	protected static abstract class Compression {
1438 
1439 		public void start(uint hubId) {
1440 
1441 			auto handler = Handler.sharedInstance();
1442 
1443 			while(true) {
1444 
1445 				auto data = receiveOnly!(uint, immutable(ubyte)[]); // tuple(order, uncompressed payload)
1446 
1447 				if(data[0] == uint.max) break;
1448 
1449 				handler.send(new HncomPlayer.OrderedGamePacket(hubId, data[0], this.compress(data[1].dup)).encode());
1450 
1451 			}
1452 
1453 		}
1454 
1455 		protected abstract ubyte[] compress(ubyte[] payload);
1456 
1457 	}
1458 
1459 }
1460 
1461 private void startCompressionImpl(T)(uint hubId) {
1462 	Thread.getThis().name = "Compression@" ~ to!string(hubId);
1463 	auto c = new T();
1464 	c.start(hubId);
1465 }
1466 
1467 enum InputMode : ubyte {
1468 
1469 	keyboard = HncomPlayer.Add.KEYBOARD,
1470 	touch = HncomPlayer.Add.TOUCH,
1471 	controller = HncomPlayer.Add.CONTROLLER,
1472 
1473 }
1474 
1475 enum DeviceOS : ubyte {
1476 
1477 	unknown = 0,
1478 	android = 1,
1479 	ios = 2,
1480 	osx = 3,
1481 	fireos = 4,
1482 	gearvr = 5,
1483 	hololens = 6,
1484 	win10 = 7,
1485 	win32 = 8,
1486 	dedicated = 9,
1487 	orbis = 10,
1488 	nx = 11
1489 
1490 }
1491 
1492 enum PermissionLevel : ubyte {
1493 
1494 	user,
1495 	operator,
1496 	host,
1497 	automation,
1498 	admin,
1499 
1500 }
1501 
1502 /**
1503  * Checks whether or not the given symbol is of a connected player class.
1504  * Returns:
1505  * 		true if the given symbol is or extends Player and not Puppet
1506  * Example:
1507  * ---
1508  * assert(isPlayer!Player);
1509  * assert(!isPlayer!Puppet);
1510  * assert(isPlayer!PocketPlayerBase);
1511  * assert(isPlayer!(MinecraftPlayer!210));
1512  * assert(!isPlayer!(Projection!Puppet));
1513  * ---
1514  */
1515 enum isPlayer(T) = is(T : Player) && !is(T : Puppet);
1516 
1517 /**
1518  * Checks if the given entity is an instance of a connected player.
1519  * Params:
1520  * 		entity = an instance of an entity
1521  * Returns:
1522  * 		true if the entity can be casted to Player and not to Puppet
1523  * Example:
1524  * ---
1525  * assert(isPlayerInstance(player!"steve"));
1526  * assert(!isPlayerInstance(new Puppet(player!"steve")));
1527  * ---
1528  */
1529 public @safe @nogc bool isPlayerInstance(Entity entity) {
1530 	return cast(Player)entity && !cast(Puppet)entity;
1531 }
1532 
1533 mixin template generateHandlers(E...) {
1534 
1535 	public override void handle(ubyte _id, ubyte[] _data) {
1536 		switch(_id) {
1537 			foreach(P ; E) {
1538 				static if(P.SERVERBOUND && (is(typeof(mixin("handle" ~ P.stringof ~ "Packet"))) || is(typeof(P.variantField)))) {
1539 					case P.ID:
1540 					{
1541 						P _packet = P.fromBuffer!false(_data);
1542 						static if(!is(typeof(P.variantField))) {
1543 							with(_packet) mixin("return this.handle" ~ P.stringof ~ "Packet(" ~ P.FIELDS.join(",") ~ ");");
1544 						} else {
1545 							switch(mixin("_packet." ~ P.variantField)) {
1546 								foreach(V ; P.Variants) {
1547 									static if(is(typeof(mixin("handle" ~ P.stringof ~ V.stringof ~ "Packet")))) {
1548 										mixin((){ import std.string:toUpper;return "case V." ~ P.variantField.toUpper ~ ":"; }()); //TODO convert to upper snake case
1549 										{
1550 											V _variant = _packet..new V();
1551 											_variant.decode();
1552 											with(_packet) { with(_variant) mixin("return this.handle" ~ P.stringof ~ V.stringof ~ "Packet(" ~ join((){ string[] f;foreach(fl;P.FIELDS){if(fl!=P.variantField){f~=fl;}}return f; }() ~ V.FIELDS, ",") ~ ");"); }
1553 										}
1554 									}
1555 								}
1556 								default: return;
1557 							}
1558 						}
1559 					}
1560 				} else version(ShowUnhandled) {
1561 					static if(P.SERVERBOUND) pragma(msg, "Packet " ~ P.stringof ~ " is not handled");
1562 				}
1563 			}
1564 			default:
1565 				version(ShowUnhandled) error_log("Unknown packet '", _id, "' with ", _data.length, " bytes");
1566 				break;
1567 		}
1568 	}
1569 
1570 }
1571 
1572 /**
1573  * Unconnected player for visualization.
1574  * In the world is registered as an entity and it will
1575  * not be found in the array of player obtained with
1576  * world.online!Player nor in the count obtained with
1577  * world.count!Player.
1578  * Example:
1579  * ---
1580  * //spawn a puppet 10 blocks over a player that follows it
1581  * class PuppetWorld : World {
1582  * 
1583  *    private Puppet[uint] puppets;
1584  * 
1585  *    public @event join(PlayerSpawnEvent event) {
1586  *       this.puppets[event.player.id] = this.spawn!Puppet(event.player.position, event.player.name, event.player.skin, event.player.uuid);
1587  *    }
1588  * 
1589  *    public @event left(PlayerDespawnEvent event) {
1590  *       this.despawn(this.puppets[event.player.id]);
1591  *       this.puppets.remove(event.player.id);
1592  *    }
1593  * 
1594  *    public @event move(PlayerMoveEvent event) {
1595  *       this.puppets[event.player.id].move(event.position, event.yaw, event.bodyYaw, event.pitch);
1596  *    }
1597  * 
1598  * }
1599  * ---
1600  * Example:
1601  * ---
1602  * // Unticked puppets will reduce the CPU usage
1603  * auto ticked = new Puppet();
1604  * auto unticked = new Unticked!Puppet();
1605  * ---
1606  */
1607 class Puppet : Player {
1608 	
1609 	public this(World world, EntityPosition position, string name, Skin skin, UUID uuid) {
1610 		//TODO create a playerinfo
1611 		super(null, world, position);
1612 	}
1613 	
1614 	public this(World world, EntityPosition position, string name, Skin skin) {
1615 		this(world, position, name, skin, world.server.nextUUID);
1616 	}
1617 	
1618 	public this(World world, EntityPosition position, string name) {
1619 		this(world, position, name, Skin.STEVE);
1620 	}
1621 	
1622 	public this(World world, Player from) {
1623 		this(world, from.position, from.name, from.skin, from.uuid);
1624 	}
1625 	
1626 	protected override @safe @nogc void sendMovementUpdates(Entity[] entities) {}
1627 	
1628 	protected override @safe @nogc void sendMotionUpdates(Entity[] entities) {}
1629 	
1630 	protected override @safe @nogc void sendTipImpl(Message[] messages) {}
1631 	
1632 	protected override @safe @nogc void sendTitleImpl(Title title, Subtitle subtitle, uint fadeIn, uint stay, uint fadeOut) {}
1633 	
1634 	protected override @safe @nogc void sendHideTitles() {}
1635 	
1636 	protected override @safe @nogc void sendResetTitles() {}
1637 	
1638 	public override @safe @nogc void sendGamemode() {}
1639 	
1640 	public override @safe @nogc void sendOpenContainer(ubyte type, ushort slots, BlockPosition position) {}
1641 	
1642 	public override @safe @nogc void spawnToItself() {}
1643 	
1644 	public override @safe @nogc void sendAddList(Player[] players) {}
1645 	
1646 	public override @safe @nogc void sendRemoveList(Player[] players) {}
1647 	
1648 	protected override @safe @nogc void sendSpawnPosition() {}
1649 	
1650 	protected override @safe @nogc void sendPosition() {}
1651 	
1652 	public override @safe @nogc void sendMetadata(Entity entity) {}
1653 	
1654 	public override @safe @nogc void sendChunk(Chunk chunk) {}
1655 	
1656 	public override @safe @nogc void unloadChunk(ChunkPosition pos) {}
1657 	
1658 	public override @safe @nogc void sendInventory(ubyte flag=PlayerInventory.ALL, bool[] slots=[]) {}
1659 	
1660 	public override @safe @nogc void sendHeld() {}
1661 	
1662 	public override @safe @nogc void sendEntityEquipment(Player player) {}
1663 	
1664 	public override @safe @nogc void sendArmorEquipment(Player player) {}
1665 	
1666 	public override @safe @nogc void sendHurtAnimation(Entity entity) {}
1667 	
1668 	public override @safe @nogc void sendDeathAnimation(Entity entity) {}
1669 	
1670 	protected override @safe @nogc void sendDeathSequence() {}
1671 	
1672 	protected override @safe @nogc void experienceUpdated() {}
1673 	
1674 	public override @safe @nogc void sendJoinPacket() {}
1675 
1676 	public override @safe @nogc void sendResourcePack() {}
1677 
1678 	public override void sendPermissionLevel(PermissionLevel permissionLevel) {}
1679 	
1680 	public override @safe @nogc void sendDifficulty(Difficulty difficulty) {}
1681 
1682 	public override @safe @nogc void sendWorldGamemode(Gamemode gamemode) {}
1683 	
1684 	public override @safe @nogc void sendSettingsPacket() {}
1685 	
1686 	public override @safe @nogc void sendRespawnPacket() {}
1687 	
1688 	public override @safe @nogc void setAsReadyToSpawn() {}
1689 	
1690 	public override @safe @nogc void sendLightning(Lightning lightning) {}
1691 	
1692 	public override @safe @nogc void sendAnimation(Entity entity) {}
1693 	
1694 	public override @safe @nogc void sendBlocks(PlacedBlock[] block) {}
1695 	
1696 	public override @safe @nogc void sendTile(Tile tiles, bool translatable) {}
1697 	
1698 	public override @safe @nogc void sendPickupItem(Entity picker, Entity picked) {}
1699 	
1700 	public override @safe @nogc void sendPassenger(ubyte mode, uint passenger, uint vehicle) {}
1701 	
1702 	public override @safe @nogc void sendExplosion(EntityPosition position, float radius, Vector3!byte[] updates) {}
1703 	
1704 	public override @safe @nogc void sendMap(Map map) {}
1705 	
1706 	public override @safe @nogc void handle(ubyte id, ubyte[] buffer) {}
1707 	
1708 }
1709 
1710 struct TitleImpl {
1711 
1712 	Message[] message;
1713 
1714 	this(E...)(E args) {
1715 		this.message = Message.convert(args);
1716 	}
1717 
1718 	alias message this;
1719 
1720 }
1721 
1722 alias Title = TitleImpl;
1723 
1724 alias Subtitle = TitleImpl;