aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp
blob: f9855c0cb99e3a243015e3a6eabe307d8291f570 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include "stdafx.h"

#include "CliCommandBan.h"

#include "..\..\ServerCliEngine.h"
#include "..\..\ServerCliParser.h"
#include "..\..\..\Access\Access.h"
#include "..\..\..\Common\StringUtils.h"
#include "..\..\..\..\Minecraft.Client\PlayerConnection.h"
#include "..\..\..\..\Minecraft.Client\ServerPlayer.h"
#include "..\..\..\..\Minecraft.World\DisconnectPacket.h"

#include <algorithm>

namespace ServerRuntime
{
	namespace
	{
		static void AppendUniqueXuid(PlayerUID xuid, std::vector<PlayerUID> *out)
		{
			if (out == nullptr || xuid == INVALID_XUID)
			{
				return;
			}

			if (std::find(out->begin(), out->end(), xuid) == out->end())
			{
				out->push_back(xuid);
			}
		}

		static void CollectPlayerBanXuids(const std::shared_ptr<ServerPlayer> &player, std::vector<PlayerUID> *out)
		{
			if (player == nullptr || out == nullptr)
			{
				return;
			}

			// Keep both identity variants because the dedicated server checks login and online XUIDs separately.
			AppendUniqueXuid(player->getXuid(), out);
			AppendUniqueXuid(player->getOnlineXuid(), out);
		}
	}

	const char *CliCommandBan::Name() const
	{
		return "ban";
	}

	const char *CliCommandBan::Usage() const
	{
		return "ban <player> [reason ...]";
	}

	const char *CliCommandBan::Description() const
	{
		return "Ban an online player.";
	}

	/**
	 * Resolves the live player, writes one or more Access ban entries, and disconnects the target with the banned reason
	 * 対象プレイヤーを解決してBANを保存し切断する
	 */
	bool CliCommandBan::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine)
	{
		if (line.tokens.size() < 2)
		{
			engine->LogWarn("Usage: ban <player> [reason ...]");
			return false;
		}
		if (!ServerRuntime::Access::IsInitialized())
		{
			engine->LogWarn("Access manager is not initialized.");
			return false;
		}

		const auto target = engine->FindPlayerByNameUtf8(line.tokens[1]);
		if (target == nullptr)
		{
			engine->LogWarn("Unknown player: " + line.tokens[1] + " (this server build can only ban players that are currently online).");
			return false;
		}

		std::vector<PlayerUID> xuids;
		CollectPlayerBanXuids(target, &xuids);
		if (xuids.empty())
		{
			engine->LogWarn("Cannot ban that player because no valid XUID is available.");
			return false;
		}

		const bool hasUnbannedIdentity = std::any_of(
			xuids.begin(),
			xuids.end(),
			[](PlayerUID xuid) { return !ServerRuntime::Access::IsPlayerBanned(xuid); });
		if (!hasUnbannedIdentity)
		{
			engine->LogWarn("That player is already banned.");
			return false;
		}

		ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console");
		metadata.reason = StringUtils::JoinTokens(line.tokens, 2);
		if (metadata.reason.empty())
		{
			metadata.reason = "Banned by an operator.";
		}

		const std::string playerName = StringUtils::WideToUtf8(target->getName());
		for (const auto xuid : xuids)
		{
			if (ServerRuntime::Access::IsPlayerBanned(xuid))
			{
				continue;
			}

			if (!ServerRuntime::Access::AddPlayerBan(xuid, playerName, metadata))
			{
				engine->LogError("Failed to write player ban.");
				return false;
			}
		}

		if (target->connection != nullptr)
		{
			target->connection->disconnect(DisconnectPacket::eDisconnect_Banned);
		}

		engine->LogInfo("Banned player " + playerName + ".");
		return true;
	}

	/**
	 * Suggests currently connected player names for the Java-style player argument
	 * プレイヤー引数の補完候補を返す
	 */
	void CliCommandBan::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector<std::string> *out) const
	{
		if (context.currentTokenIndex == 1)
		{
			engine->SuggestPlayers(context.prefix, context.linePrefix, out);
		}
	}
}