/** * Disconnects the session with the specified reason. This causes a KickMessage to be sent. When * it has been delivered, the channel is closed. * * @param reason The reason for disconnection. * @param overrideKick Whether to skip the kick event. */ public void disconnect(String reason, boolean overrideKick) { if (player != null && !overrideKick) { PlayerKickEvent event = EventFactory.onPlayerKick(player, reason); if (event.isCancelled()) { return; } reason = event.getReason(); if (player.isOnline() && event.getLeaveMessage() != null) { server.broadcastMessage(event.getLeaveMessage()); } } // log that the player was kicked if (player != null) { GlowServer.logger.info(player.getName() + " kicked: " + reason); } else { GlowServer.logger.info("[" + address + "] kicked: " + reason); } if (quitReason == null) { quitReason = "kicked"; } // perform the kick, sending a kick message if possible if (isActive() && (getProtocol() instanceof LoginProtocol || getProtocol() instanceof PlayProtocol)) { // channel is both currently connected and in a protocol state allowing kicks sendWithFuture(new KickMessage(reason)).addListener(ChannelFutureListener.CLOSE); } else { getChannel().close(); } }
@Override public void handle(Session session, GlowPlayer player, KickMessage message) { String text = EventFactory.onPlayerQuit(player).getQuitMessage(); if (message != null) { session.getServer().broadcastMessage(text); } session.disconnect("Goodbye!", true); }
private void putVine(GlowBlock block, Vine vine, GlowBlock fromBlock) { GlowBlockState state = block.getState(); state.setType(Material.VINE); state.setData(vine); if (fromBlock != null) { BlockSpreadEvent spreadEvent = new BlockSpreadEvent(block, fromBlock, state); EventFactory.callEvent(spreadEvent); if (!spreadEvent.isCancelled()) { state.update(true); } } else { state.update(true); } }
/** Pulse this session, performing any updates needed. */ void pulse() { // drop the previous placement if needed if (previousPlacementTicks > 0 && --previousPlacementTicks == 0) { previousPlacement = null; } // process messages Message message; while ((message = messageQueue.poll()) != null) { if (disconnected) { // disconnected, we are just seeing extra messages now continue; } super.messageReceived(message); } // check if the client is disconnected if (disconnected) { connectionManager.sessionInactivated(this); if (player == null) { return; } player.remove(); Message userListMessage = UserListItemMessage.removeOne(player.getUniqueId()); for (GlowPlayer player : server.getOnlinePlayers()) { if (player.canSee(this.player)) { player.getSession().send(userListMessage); } else { player.stopHidingDisconnectedPlayer(this.player); } } GlowServer.logger.info(player.getName() + " [" + address + "] lost connection"); if (player.isSleeping()) { player.leaveBed(false); } final String text = EventFactory.onPlayerQuit(player).getQuitMessage(); if (online && text != null && !text.isEmpty()) { server.broadcastMessage(text); } player = null; // in case we are disposed twice } }
@Override public void updateBlock(GlowBlock block) { if (isNearWater(block) || GlowBiomeClimate.isRainy(block)) { block.setData((byte) 7); // set this block as fully wet } else if (block.getData() > 0) { block.setData((byte) (block.getData() - 1)); // if this block is wet, it becomes less wet } else if (!Arrays.asList(possibleCrops).contains(block.getRelative(BlockFace.UP).getType())) { // turns block back to dirt if nothing is planted on final GlowBlockState state = block.getState(); state.setType(Material.DIRT); state.setRawData((byte) 0); BlockFadeEvent fadeEvent = new BlockFadeEvent(block, state); EventFactory.callEvent(fadeEvent); if (!fadeEvent.isCancelled()) { state.update(true); } } }
@Override public void handle(GlowSession session, DiggingMessage message) { // Todo: Implement SHOOT_ARROW_FINISH_EATING // Todo: Implement SWAP_ITEM_IN_HAND GlowPlayer player = session.getPlayer(); GlowWorld world = player.getWorld(); GlowBlock block = world.getBlockAt(message.getX(), message.getY(), message.getZ()); BlockFace face = BlockPlacementHandler.convertFace(message.getFace()); ItemStack holding = player.getItemInHand(); if (block.getRelative(face).getType() == Material.FIRE) { block.getRelative(face).breakNaturally(); return; // returns to avoid breaking block in creative } boolean blockBroken = false; boolean revert = false; if (message.getState() == DiggingMessage.START_DIGGING || player.getDigging() == null) { // call interact event Action action = Action.LEFT_CLICK_BLOCK; Block eventBlock = block; if (player.getLocation().distanceSquared(block.getLocation()) > 36 || block.getTypeId() == 0) { action = Action.LEFT_CLICK_AIR; eventBlock = null; } PlayerInteractEvent interactEvent = EventFactory.onPlayerInteract(player, action, eventBlock, face); // blocks don't get interacted with on left click, so ignore that // attempt to use item in hand, that is, dig up the block if (!BlockPlacementHandler.selectResult(interactEvent.useItemInHand(), true)) { // the event was cancelled, get out of here revert = true; } else if (player.getGameMode() != GameMode.SPECTATOR) { player.setDigging(null); // emit damage event - cancel by default if holding a sword boolean instaBreak = player.getGameMode() == GameMode.CREATIVE || block.getMaterialValues().getHardness() == 0; BlockDamageEvent damageEvent = new BlockDamageEvent(player, block, player.getItemInHand(), instaBreak); if (player.getGameMode() == GameMode.CREATIVE && holding != null && EnchantmentTarget.WEAPON.includes(holding.getType())) { damageEvent.setCancelled(true); } EventFactory.callEvent(damageEvent); // follow orders if (damageEvent.isCancelled()) { revert = true; } else { // in creative, break even if denied in the event, or the block // can never be broken (client does not send DONE_DIGGING). blockBroken = damageEvent.getInstaBreak(); if (!blockBroken) { /// TODO: add a delay here based on hardness player.setDigging(block); } } } } else if (message.getState() == DiggingMessage.FINISH_DIGGING) { // shouldn't happen in creative mode // todo: verification against malicious clients blockBroken = block.equals(player.getDigging()); if (blockBroken && holding.getType() != Material.AIR && holding.getDurability() != holding.getType().getMaxDurability()) { switch (block.getType()) { case GRASS: case DIRT: case SAND: case GRAVEL: case MYCEL: case SOUL_SAND: switch (holding.getType()) { case WOOD_SPADE: case STONE_SPADE: case IRON_SPADE: case GOLD_SPADE: case DIAMOND_SPADE: holding.setDurability((short) (holding.getDurability() + 1)); break; default: holding.setDurability((short) (holding.getDurability() + 2)); break; } break; case LOG: case LOG_2: case WOOD: case CHEST: switch (holding.getType()) { case WOOD_AXE: case STONE_AXE: case IRON_AXE: case GOLD_AXE: case DIAMOND_AXE: holding.setDurability((short) (holding.getDurability() + 1)); break; default: holding.setDurability((short) (holding.getDurability() + 2)); break; } break; case STONE: case COBBLESTONE: break; default: holding.setDurability((short) (holding.getDurability() + 2)); break; } if (holding.getDurability() >= holding.getType().getMaxDurability()) { player.getInventory().remove(holding); // player.getItemInHand().setType(Material.AIR); } } player.setDigging(null); } else if (message.getState() == DiggingMessage.STATE_DROP_ITEM) { player.dropItemInHand(false); return; } else if (message.getState() == DiggingMessage.STATE_DROP_ITEMSTACK) { player.dropItemInHand(true); return; } else if (message.getState() == DiggingMessage.STATE_SHOT_ARROW_FINISH_EATING && player.getUsageItem() != null) { if (player.getUsageItem().equals(holding)) { ItemType type = ItemTable.instance().getItem(player.getUsageItem().getType()); ((ItemTimedUsage) type).endUse(player, player.getUsageItem()); } else { // todo: verification against malicious clients // todo: inform player their item is wrong } return; } else { return; } if (blockBroken && !revert) { // fire the block break event BlockBreakEvent breakEvent = EventFactory.callEvent(new BlockBreakEvent(block, player)); if (breakEvent.isCancelled()) { BlockPlacementHandler.revert(player, block); return; } BlockType blockType = ItemTable.instance().getBlock(block.getType()); if (blockType != null) { blockType.blockDestroy(player, block, face); } // destroy the block if (!block.isEmpty() && !block.isLiquid() && player.getGameMode() != GameMode.CREATIVE && world.getGameRuleMap().getBoolean("doTileDrops")) { for (ItemStack drop : block.getDrops(holding)) { GlowItem item = world.dropItemNaturally(block.getLocation(), drop); item.setPickupDelay(30); item.setBias(player); } } player.addExhaustion(0.025f); // STEP_SOUND actually is the block break particles world.playEffectExceptTo( block.getLocation(), Effect.STEP_SOUND, block.getTypeId(), 64, player); GlowBlockState state = block.getState(); block.setType(Material.AIR); if (blockType != null) { blockType.afterDestroy(player, block, face, state); } } else if (revert) { // replace the block that wasn't really dug BlockPlacementHandler.revert(player, block); } else if (block.getType() != Material.AIR) { BlockType blockType = ItemTable.instance().getBlock(block.getType()); blockType.leftClickBlock(player, block, holding); } }
/** * Sets the player associated with this session. * * @param profile The player's profile with name and UUID information. * @throws IllegalStateException if there is already a player associated with this session. */ public void setPlayer(PlayerProfile profile) { if (player != null) { throw new IllegalStateException("Cannot set player twice"); } // isActive check here in case player disconnected during authentication if (!isActive()) { // no need to call onDisconnect() since it only does anything if there's a player set return; } // initialize the player PlayerDataService.PlayerReader reader = server.getPlayerDataService().beginReadingData(profile.getUniqueId()); player = new GlowPlayer(this, profile, reader); // isActive check here in case player disconnected after authentication, // but before the GlowPlayer initialization was completed if (!isActive()) { onDisconnect(); return; } // login event PlayerLoginEvent event = EventFactory.onPlayerLogin(player, hostname); if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { disconnect(event.getKickMessage(), true); return; } // joins the player player.join(this, reader); // Kick other players with the same UUID for (GlowPlayer other : getServer().getOnlinePlayers()) { if (other != player && other.getUniqueId().equals(player.getUniqueId())) { other.getSession().disconnect("You logged in from another location.", true); break; } } player.getWorld().getRawPlayers().add(player); online = true; GlowServer.logger.info( player.getName() + " [" + address + "] connected, UUID: " + player.getUniqueId()); // message and user list String message = EventFactory.onPlayerJoin(player).getJoinMessage(); if (message != null && !message.isEmpty()) { server.broadcastMessage(message); } // todo: display names are included in the outgoing messages here, but // don't show up on the client. A workaround or proper fix is needed. Message addMessage = new UserListItemMessage(UserListItemMessage.Action.ADD_PLAYER, player.getUserListEntry()); List<UserListItemMessage.Entry> entries = new ArrayList<>(); for (GlowPlayer other : server.getOnlinePlayers()) { if (other != player && other.canSee(player)) { other.getSession().send(addMessage); } if (player.canSee(other)) { entries.add(other.getUserListEntry()); } } send(new UserListItemMessage(UserListItemMessage.Action.ADD_PLAYER, entries)); }