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