summaryrefslogtreecommitdiffstats
path: root/src/BlockTypeRegistry.h
blob: 529e13eb27eeefc05c1f8f734a5c389b67ab2866 (plain) (blame)
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#pragma once





#include <map>
#include <functional>





// fwd:
class cBlockHandler;
class BlockState;





/** Complete information about a single block type.
The BlockTypeRegistry uses this structure to store the registered information. */
class BlockInfo
{
public:

	/** Callback is used to query block hints dynamically, based on the current BlockState.
	Useful for example for redstone lamps that can be turned on or off. */
	using HintCallback = std::function<AString(const AString & aTypeName, const BlockState & aBlockState)>;


	/** Creates a new instance with the specified BlockTypeName and handler / hints / callbacks.
	aPluginName specifies the name of the plugin to associate with the block type (to allow unload / reload). */
	BlockInfo(
		const AString & aPluginName,
		const AString & aBlockTypeName,
		std::shared_ptr<cBlockHandler> aHandler,
		const std::map<AString, AString> & aHints = std::map<AString, AString>(),
		const std::map<AString, HintCallback> & aHintCallbacks = std::map<AString, HintCallback>()
	);


	/** Retrieves the value associated with the specified hint for this specific BlockTypeName and BlockState.
	Queries hint callbacks first, then static hints if a callback doesn't exist.
	Returns an empty string if hint not found at all. */
	AString hintValue(
		const AString & aHintName,
		const BlockState & aBlockState
	);

	// Simple getters:
	const AString & pluginName() const { return mPluginName; }
	const AString & blockTypeName() const { return mBlockTypeName; }
	std::shared_ptr<cBlockHandler> handler() const { return mHandler; }

	/** Sets (creates or updates) a static hint.
	Hints provided by callbacks are unaffected by this - callbacks are "higher priority", they overwrite anything set here.
	Logs an info message if the hint is already provided by a hint callback. */
	void setHint(const AString & aHintKey, const AString & aHintValue);

	/** Removes a hint.
	Silently ignored if the hint hasn't been previously set. */
	void removeHint(const AString & aHintKey);


private:

	/** The name of the plugin that registered the block. */
	AString mPluginName;

	/** The name of the block type, such as "minecraft:redstone_lamp" */
	AString mBlockTypeName;

	/** The callbacks to call for various interaction. */
	std::shared_ptr<cBlockHandler> mHandler;

	/** Optional static hints for any subsystem to use, such as "IsSnowable" -> "1".
	Hint callbacks are of higher priority than mHints - if a hint is provided by a mHintCallback, its value in mHints is ignored. */
	std::map<AString, AString> mHints;

	/** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState).
	Hint callbacks are of higher priority than mHints - if a hint is provided by a mHintCallback, its value in mHints is ignored. */
	std::map<AString, HintCallback> mHintCallbacks;
};





/** Stores information on all known block types.
Can dynamically add and remove block types.
Block types are identified using BlockTypeName.
Supports unregistering and re-registering the same type by the same plugin.
Stores the name of the plugin that registered the type, for better plugin error messages ("already registered in X")
and so that we can unload and reload plugins. */
class BlockTypeRegistry
{
public:
	// fwd:
	class AlreadyRegisteredException;
	class NotRegisteredException;


	/** Creates an empty new instance of the block type registry */
	BlockTypeRegistry() = default;

	/** Registers the specified block type.
	If the block type already exists and the plugin is the same, updates the registration.
	If the block type already exists and the plugin is different, throws an AlreadyRegisteredException. */
	void registerBlockType(
		const AString & aPluginName,
		const AString & aBlockTypeName,
		std::shared_ptr<cBlockHandler> aHandler,
		const std::map<AString, AString> & aHints = std::map<AString, AString>(),
		const std::map<AString, BlockInfo::HintCallback> & aHintCallbacks = std::map<AString, BlockInfo::HintCallback>()
	);

	/** Returns the registration information for the specified BlockTypeName.
	Returns nullptr if BlockTypeName not found. */
	std::shared_ptr<BlockInfo> blockInfo(const AString & aBlockTypeName);

	/** Removes all registrations done by the specified plugin. */
	void removeAllByPlugin(const AString & aPluginName);

	/** Sets (adds or overwrites) a single Hint value for a BlockType.
	Throws NotRegisteredException if the BlockTypeName is not registered. */
	void setBlockTypeHint(
		const AString & aBlockTypeName,
		const AString & aHintKey,
		const AString & aHintValue
	);

	/** Removes a previously registered single Hint value for a BlockType.
	Throws NotRegisteredException if the BlockTypeName is not registered.
	Silently ignored if the Hint hasn't been previously set. */
	void removeBlockTypeHint(
		const AString & aBlockTypeName,
		const AString & aHintKey
	);


private:

	/** The actual block type registry.
	Maps the BlockTypeName to the BlockInfo instance. */
	std::map<AString, std::shared_ptr<BlockInfo>> mRegistry;

	/** The CS that protects mRegistry against multithreaded access. */
	cCriticalSection mCSRegistry;
};





/** The exception thrown from BlockTypeRegistry::registerBlockType() if the same block type is being registered from a different plugin. */
class BlockTypeRegistry::AlreadyRegisteredException: public std::runtime_error
{
	using Super = std::runtime_error;

public:

	/** Creates a new instance of the exception that provides info on both the original registration and the newly attempted
	registration that caused the failure. */
	AlreadyRegisteredException(
		std::shared_ptr<BlockInfo> aPreviousRegistration,
		std::shared_ptr<BlockInfo> aNewRegistration
	);

	// Simple getters:
	std::shared_ptr<BlockInfo> previousRegistration() const { return mPreviousRegistration; }
	std::shared_ptr<BlockInfo> newRegistration()      const { return mNewRegistration; }


private:

	std::shared_ptr<BlockInfo> mPreviousRegistration;
	std::shared_ptr<BlockInfo> mNewRegistration;


	/** Returns the general exception message formatted by the two registrations.
	The output is used when logging. */
	static AString message(
		std::shared_ptr<BlockInfo> aPreviousRegistration,
		std::shared_ptr<BlockInfo> aNewRegistration
	);
};





/** The exception thrown from BlockTypeRegistry::setBlockTypeHint() if the block type has not been registered before. */
class BlockTypeRegistry::NotRegisteredException: public std::runtime_error
{
	using Super = std::runtime_error;

public:

	/** Creates a new instance of the exception that provides info on both the original registration and the newly attempted
	registration that caused the failure. */
	NotRegisteredException(
		const AString & aBlockTypeName,
		const AString & aHintKey,
		const AString & aHintValue
	);

	// Simple getters:
	const AString & blockTypeName() const { return mBlockTypeName; }


private:

	const AString mBlockTypeName;
};