summaryrefslogtreecommitdiffstats
path: root/src/BlockTypeRegistry.h
blob: 6a24445c54dc1ec03e7822a36f6ae45cd1de347c (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
#pragma once





#include <map>





// fwd:
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 callbacks first, then 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; }


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 hints for any subsystem to use, such as "IsSnowable" -> "1". */
	std::map<AString, AString> mHints;

	/** The callbacks for dynamic evaluation of hints, such as "LightValue" -> function(BlockTypeName, BlockState). */
	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;


	/** 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);


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
	);
};