aboutsummaryrefslogtreecommitdiff
path: root/src/unittest/test_serialization.cpp
blob: 660d77d02fe9eecf2d8e8597550bd9cec519cb08 (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
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "test.h"

#include "util/string.h"
#include "util/serialize.h"
#include <cmath>

class TestSerialization : public TestBase {
public:
	TestSerialization() { TestManager::registerTestModule(this); }
	const char *getName() { return "TestSerialization"; }

	void runTests(IGameDef *gamedef);
	void buildTestStrings();

	void testSerializeString();
	void testSerializeLongString();
	void testSerializeJsonString();
	void testDeSerializeString();
	void testDeSerializeLongString();
	void testStreamRead();
	void testStreamWrite();
	void testFloatFormat();

	std::string teststring2;
	std::wstring teststring2_w;
	std::string teststring2_w_encoded;

	static const u8 test_serialized_data[12 * 11 - 2];
};

static TestSerialization g_test_instance;

void TestSerialization::runTests(IGameDef *gamedef)
{
	buildTestStrings();

	TEST(testSerializeString);
	TEST(testDeSerializeString);
	TEST(testSerializeLongString);
	TEST(testDeSerializeLongString);
	TEST(testSerializeJsonString);
	TEST(testStreamRead);
	TEST(testStreamWrite);
	TEST(testFloatFormat);
}

////////////////////////////////////////////////////////////////////////////////

// To be used like this:
//   mkstr("Some\0string\0with\0embedded\0nuls")
// since std::string("...") doesn't work as expected in that case.
template<size_t N> std::string mkstr(const char (&s)[N])
{
	return std::string(s, N - 1);
}

void TestSerialization::buildTestStrings()
{
	std::ostringstream tmp_os;
	std::wostringstream tmp_os_w;
	std::ostringstream tmp_os_w_encoded;
	for (int i = 0; i < 256; i++) {
		tmp_os << (char)i;
		tmp_os_w << (wchar_t)i;
		tmp_os_w_encoded << (char)0 << (char)i;
	}
	teststring2 = tmp_os.str();
	teststring2_w = tmp_os_w.str();
	teststring2_w_encoded = tmp_os_w_encoded.str();
}

void TestSerialization::testSerializeString()
{
	// Test blank string
	UASSERT(serializeString16("") == mkstr("\0\0"));

	// Test basic string
	UASSERT(serializeString16("Hello world!") == mkstr("\0\14Hello world!"));

	// Test character range
	UASSERT(serializeString16(teststring2) == mkstr("\1\0") + teststring2);
}

void TestSerialization::testDeSerializeString()
{
	// Test deserialize
	{
		std::istringstream is(serializeString16(teststring2), std::ios::binary);
		UASSERT(deSerializeString16(is) == teststring2);
		UASSERT(!is.eof());
		is.get();
		UASSERT(is.eof());
	}

	// Test deserialize an incomplete length specifier
	{
		std::istringstream is(mkstr("\x53"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
	}

	// Test deserialize a string with incomplete data
	{
		std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString16(is));
	}
}

void TestSerialization::testSerializeLongString()
{
	// Test blank string
	UASSERT(serializeString32("") == mkstr("\0\0\0\0"));

	// Test basic string
	UASSERT(serializeString32("Hello world!") == mkstr("\0\0\0\14Hello world!"));

	// Test character range
	UASSERT(serializeString32(teststring2) == mkstr("\0\0\1\0") + teststring2);
}

void TestSerialization::testDeSerializeLongString()
{
	// Test deserialize
	{
		std::istringstream is(serializeString32(teststring2), std::ios::binary);
		UASSERT(deSerializeString32(is) == teststring2);
		UASSERT(!is.eof());
		is.get();
		UASSERT(is.eof());
	}

	// Test deserialize an incomplete length specifier
	{
		std::istringstream is(mkstr("\x53"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
	}

	// Test deserialize a string with incomplete data
	{
		std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
	}

	// Test deserialize a string with a length too large
	{
		std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
		EXCEPTION_CHECK(SerializationError, deSerializeString32(is));
	}
}


void TestSerialization::testSerializeJsonString()
{
	// Test blank string
	UASSERT(serializeJsonString("") == "\"\"");

	// Test basic string
	UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\"");

	// MSVC fails when directly using "\\\\"
	std::string backslash = "\\";
	UASSERT(serializeJsonString(teststring2) ==
		mkstr("\"") +
		"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
		"\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
		"\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
		"\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
		" !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
		"\\/" + teststring2.substr(0x30, 0x5c-0x30) +
		backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
		"\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
		"\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
		"\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
		"\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
		"\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
		"\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
		"\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
		"\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
		"\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
		"\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
		"\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
		"\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
		"\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
		"\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
		"\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
		"\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
		"\"");

	// Test deserialize
	std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
	UASSERT(deSerializeJsonString(is) == teststring2);
	UASSERT(!is.eof());
	is.get();
	UASSERT(is.eof());
}


void TestSerialization::testStreamRead()
{
	std::string datastr(
		(const char *)test_serialized_data,
		sizeof(test_serialized_data));
	std::istringstream is(datastr, std::ios_base::binary);

	UASSERT(readU8(is) == 0x11);
	UASSERT(readU16(is) == 0x2233);
	UASSERT(readU32(is) == 0x44556677);
	UASSERT(readU64(is) == 0x8899AABBCCDDEEFFLL);

	UASSERT(readS8(is) == -128);
	UASSERT(readS16(is) == 30000);
	UASSERT(readS32(is) == -6);
	UASSERT(readS64(is) == -43);

	UASSERT(readF1000(is) == 53.534f);
	UASSERT(readF1000(is) == -300000.32f);
	UASSERT(readF1000(is) == F1000_MIN);
	UASSERT(readF1000(is) == F1000_MAX);

	UASSERT(deSerializeString16(is) == "foobar!");

	UASSERT(readV2S16(is) == v2s16(500, 500));
	UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
	UASSERT(readV2S32(is) == v2s32(1920, 1080));
	UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));

	UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
	UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));

	UASSERT(deSerializeString32(is) == "some longer string here");

	UASSERT(is.rdbuf()->in_avail() == 2);
	UASSERT(readU16(is) == 0xF00D);
	UASSERT(is.rdbuf()->in_avail() == 0);
}


void TestSerialization::testStreamWrite()
{
	std::ostringstream os(std::ios_base::binary);
	std::string data;

	writeU8(os, 0x11);
	writeU16(os, 0x2233);
	writeU32(os, 0x44556677);
	writeU64(os, 0x8899AABBCCDDEEFFLL);

	writeS8(os, -128);
	writeS16(os, 30000);
	writeS32(os, -6);
	writeS64(os, -43);

	writeF1000(os, 53.53467f);
	writeF1000(os, -300000.32f);
	writeF1000(os, F1000_MIN);
	writeF1000(os, F1000_MAX);

	os << serializeString16("foobar!");

	data = os.str();
	UASSERT(data.size() < sizeof(test_serialized_data));
	UASSERT(!memcmp(&data[0], test_serialized_data, data.size()));

	writeV2S16(os, v2s16(500, 500));
	writeV3S16(os, v3s16(4207, 604, -30));
	writeV2S32(os, v2s32(1920, 1080));
	writeV3S32(os, v3s32(-400, 6400054, 290549855));

	writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
	writeARGB8(os, video::SColor(255, 128, 50, 128));

	os << serializeString32("some longer string here");

	writeU16(os, 0xF00D);

	data = os.str();
	UASSERT(data.size() == sizeof(test_serialized_data));
	UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data)));
}


void TestSerialization::testFloatFormat()
{
	FloatType type = getFloatSerializationType();
	u32 i;
	f32 fs, fm;

	// Check precision of float calculations on this platform
	const std::unordered_map<f32, u32> float_results = {
		{  0.0f, 0x00000000UL },
		{  1.0f, 0x3F800000UL },
		{ -1.0f, 0xBF800000UL },
		{  0.1f, 0x3DCCCCCDUL },
		{ -0.1f, 0xBDCCCCCDUL },
		{ 1945329.25f, 0x49ED778AUL },
		{ -23298764.f, 0xCBB1C166UL },
		{  0.5f, 0x3F000000UL },
		{ -0.5f, 0xBF000000UL }
	};
	for (const auto &v : float_results) {
		i = f32Tou32Slow(v.first);
		if (std::abs((s64)v.second - i) > 32) {
			printf("Inaccurate float values on %.9g, expected 0x%X, actual 0x%X\n",
				v.first, v.second, i);
			UASSERT(false);
		}

		fs = u32Tof32Slow(v.second);
		if (std::fabs(v.first - fs) > std::fabs(v.first * 0.000005f)) {
			printf("Inaccurate float values on 0x%X, expected %.9g, actual 0x%.9g\n",
				v.second, v.first, fs);
			UASSERT(false);
		}
	}

	if (type == FLOATTYPE_SLOW) {
		// conversion using memcpy is not possible
		// Skip exact float comparison checks below
		return;
	}

	// The code below compares the IEEE conversion functions with a
	// known good IEC559/IEEE754 implementation. This test neeeds
	// IEC559 compliance in the compiler.
#if defined(__GNUC__) && (!defined(__STDC_IEC_559__) || defined(__FAST_MATH__))
	// GNU C++ lies about its IEC559 support when -ffast-math is active.
	// https://gcc.gnu.org/bugzilla//show_bug.cgi?id=84949
	bool is_iec559 = false;
#else
	bool is_iec559 = std::numeric_limits<f32>::is_iec559;
#endif
	if (!is_iec559)
		return;

	auto test_single = [&fs, &fm](const u32 &i) -> bool {
		memcpy(&fm, &i, 4);
		fs = u32Tof32Slow(i);
		if (fm != fs) {
			printf("u32Tof32Slow failed on 0x%X, expected %.9g, actual %.9g\n",
				i, fm, fs);
			return false;
		}
		if (f32Tou32Slow(fs) != i) {
			printf("f32Tou32Slow failed on %.9g, expected 0x%X, actual 0x%X\n",
				fs, i, f32Tou32Slow(fs));
			return false;
		}
		return true;
	};

	// Use step of prime 277 to speed things up from 3 minutes to a few seconds
	// Test from 0 to 0xFF800000UL (positive)
	for (i = 0x00000000UL; i <= 0x7F800000UL; i += 277)
		UASSERT(test_single(i));

	// Ensure +inf and -inf are tested
	UASSERT(test_single(0x7F800000UL));
	UASSERT(test_single(0xFF800000UL));

	// Test from 0x80000000UL to 0xFF800000UL (negative)
	for (i = 0x80000000UL; i <= 0xFF800000UL; i += 277)
		UASSERT(test_single(i));
}

const u8 TestSerialization::test_serialized_data[12 * 11 - 2] = {
	0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
	0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
	0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07,
	0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
	0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
	0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
	0x5f, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
	0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
	0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
	0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
};
pt">; // Add to mesh mesh->addMeshBuffer(buf); buf->drop(); } { // Back scene::IMeshBuffer *buf = new scene::SMeshBuffer(); video::SColor c(255,255,255,255); video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1), video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1), video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0), video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0), }; u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material buf->getMaterial().setFlag(video::EMF_LIGHTING, false); //buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false); buf->getMaterial().setTexture (0, driver->getTexture(getTexturePath("sign_back.png").c_str())); buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true); buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; // Add to mesh mesh->addMeshBuffer(buf); buf->drop(); } m_node = smgr->addMeshSceneNode(mesh, NULL); mesh->drop(); updateSceneNode(); } virtual void removeFromScene() { if(m_node != NULL) { m_node->remove(); m_node = NULL; } } virtual void updateLight(u8 light_at_pos) { if(m_node == NULL) return; u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); scene::IMesh *mesh = m_node->getMesh(); u16 mc = mesh->getMeshBufferCount(); for(u16 j=0; j<mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); u16 vc = buf->getVertexCount(); for(u16 i=0; i<vc; i++) { vertices[i].Color = color; } } } #endif virtual std::string infoText() { return std::string("\"") + m_text + "\""; } virtual std::string getInventoryString() { return std::string("Sign ")+m_text; } /* Special methods */ void updateSceneNode() { #ifndef SERVER if(m_node != NULL) { m_node->setPosition(getAbsolutePos()); m_node->setRotation(v3f(0, m_yaw, 0)); } #endif } void setText(std::string text) { if(text.size() > SIGN_TEXT_MAX_LENGTH) text = text.substr(0, SIGN_TEXT_MAX_LENGTH); m_text = text; setBlockChanged(); } std::string getText() { return m_text; } void setYaw(f32 yaw) { m_yaw = yaw; setBlockChanged(); } protected: scene::IMeshSceneNode *m_node; std::string m_text; f32 m_yaw; }; class RatObject : public MovingObject { public: RatObject(MapBlock *block, s16 id, v3f pos): MovingObject(block, id, pos), m_node(NULL) { m_collision_box = new core::aabbox3d<f32> (-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3); m_selection_box = new core::aabbox3d<f32> (-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3); m_yaw = 0; m_counter1 = 0; m_counter2 = 0; m_age = 0; } virtual ~RatObject() { delete m_collision_box; delete m_selection_box; } /* Implementation interface */ virtual u16 getTypeId() const { return MAPBLOCKOBJECT_TYPE_RAT; } virtual void serialize(std::ostream &os, u8 version) { MovingObject::serialize(os, version); u8 buf[2]; // Write yaw * 10 writeS16(buf, m_yaw * 10); os.write((char*)buf, 2); } virtual void update(std::istream &is, u8 version) { MovingObject::update(is, version); u8 buf[2]; // Read yaw * 10 is.read((char*)buf, 2); s16 yaw_i = readS16(buf); m_yaw = (f32)yaw_i / 10; updateNodePos(); } virtual bool serverStep(float dtime, u32 daynight_ratio) { m_age += dtime; if(m_age > 60) // Die return true; v3f dir(cos(m_yaw/180*PI),0,sin(m_yaw/180*PI)); f32 speed = 2*BS; m_speed.X = speed * dir.X; m_speed.Z = speed * dir.Z; if(m_touching_ground && (m_oldpos - m_pos).getLength() < dtime*speed/2) { m_counter1 -= dtime; if(m_counter1 < 0.0) { m_counter1 += 1.0; m_speed.Y = 5.0*BS; } } { m_counter2 -= dtime; if(m_counter2 < 0.0) { m_counter2 += (float)(myrand()%100)/100*3.0; m_yaw += ((float)(myrand()%200)-100)/100*180; m_yaw = wrapDegrees(m_yaw); } } m_oldpos = m_pos; //m_yaw += dtime*90; move(dtime, v3f(0, -9.81*BS, 0)); //updateNodePos(); return false; } #ifndef SERVER virtual void clientStep(float dtime) { //m_pos += m_speed * dtime; MovingObject::simpleMove(dtime); updateNodePos(); } virtual void addToScene(scene::ISceneManager *smgr); virtual void removeFromScene() { if(m_node == NULL) return; m_node->remove(); m_node = NULL; } virtual void updateLight(u8 light_at_pos) { if(m_node == NULL) return; u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); scene::IMesh *mesh = m_node->getMesh(); u16 mc = mesh->getMeshBufferCount(); for(u16 j=0; j<mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); u16 vc = buf->getVertexCount(); for(u16 i=0; i<vc; i++) { vertices[i].Color = color; } } } #endif virtual std::string getInventoryString() { // There must be a space after the name // Or does there? return std::string("Rat "); } /* Special methods */ void updateNodePos() { if(m_node == NULL) return; m_node->setPosition(getAbsoluteShowPos()); m_node->setRotation(v3f(0, -m_yaw+180, 0)); } protected: scene::IMeshSceneNode *m_node; float m_yaw; float m_counter1; float m_counter2; float m_age; }; /* An object on the map that represents an inventory item */ class InventoryItem; class ItemObject : public MapBlockObject { public: // The constructor of every MapBlockObject should be like this ItemObject(MapBlock *block, s16 id, v3f pos): MapBlockObject(block, id, pos), m_node(NULL) { /*m_selection_box = new core::aabbox3d<f32> (-BS*0.4,-BS*0.5,-BS*0.4, BS*0.4,BS*0.5,BS*0.4);*/ m_selection_box = new core::aabbox3d<f32> (-BS/3,-BS/2,-BS/3, BS/3,-BS/2+BS*2/3,BS/3); m_yaw = 0.0; } virtual ~ItemObject() { delete m_selection_box; } /* Implementation interface */ virtual u16 getTypeId() const { return MAPBLOCKOBJECT_TYPE_ITEM; } virtual void serialize(std::ostream &os, u8 version) { serializeBase(os, version); u8 buf[2]; // Write text length writeU16(buf, m_itemstring.size()); os.write((char*)buf, 2); // Write text os.write(m_itemstring.c_str(), m_itemstring.size()); } virtual void update(std::istream &is, u8 version) { u8 buf[2]; // Read text length is.read((char*)buf, 2); u16 size = readU16(buf); // Read text std::string old_itemstring = m_itemstring; m_itemstring.clear(); for(u16 i=0; i<size; i++) { is.read((char*)buf, 1); m_itemstring += buf[0]; } #ifndef SERVER if(m_itemstring != old_itemstring && m_node) { /* Update texture */ video::ITexture *texture = getItemImage(); scene::IMesh *mesh = m_node->getMesh(); if(mesh->getMeshBufferCount() >= 1) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(0); //dstream<<"Setting texture "<<texture<<std::endl; buf->getMaterial().setTexture(0, texture); } } updateSceneNode(); #endif } virtual bool serverStep(float dtime, u32 daynight_ratio) { return false; } #ifndef SERVER virtual void clientStep(float dtime) { m_yaw += dtime * 60; if(m_yaw >= 360.) m_yaw -= 360.; updateSceneNode(); } virtual void addToScene(scene::ISceneManager *smgr); virtual void removeFromScene() { if(m_node != NULL) { m_node->remove(); m_node = NULL; } } virtual void updateLight(u8 light_at_pos) { if(m_node == NULL) return; u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); scene::IMesh *mesh = m_node->getMesh(); u16 mc = mesh->getMeshBufferCount(); for(u16 j=0; j<mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); u16 vc = buf->getVertexCount(); for(u16 i=0; i<vc; i++) { vertices[i].Color = color; } } } #endif virtual std::string infoText() { return std::string("\"") + m_itemstring + "\""; } virtual std::string getInventoryString() { return std::string("ItemObj ")+m_itemstring; } /* Special methods */ InventoryItem * createInventoryItem(); #ifndef SERVER video::ITexture * getItemImage(); void updateSceneNode() { if(m_node != NULL) { m_node->setPosition(getAbsolutePos()); m_node->setRotation(v3f(0, m_yaw, 0)); } } #endif void setItemString(std::string inventorystring) { m_itemstring = inventorystring; setBlockChanged(); } std::string getItemString() { return m_itemstring; } protected: scene::IMeshSceneNode *m_node; std::string m_itemstring; f32 m_yaw; }; /* NOTE: Not used. */ class PlayerObject : public MovingObject { public: PlayerObject(MapBlock *block, s16 id, v3f pos): MovingObject(block, id, pos), m_node(NULL), m_yaw(0) { m_collision_box = new core::aabbox3d<f32> (-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3); /*m_selection_box = new core::aabbox3d<f32> (-BS*0.3,-BS*.25,-BS*0.3, BS*0.3,BS*0.25,BS*0.3);*/ } virtual ~PlayerObject() { if(m_collision_box) delete m_collision_box; if(m_selection_box) delete m_selection_box; } /* Implementation interface */ virtual u16 getTypeId() const { return MAPBLOCKOBJECT_TYPE_PLAYER; } virtual void serialize(std::ostream &os, u8 version) { // Object data is generated from actual player } virtual void update(std::istream &is, u8 version) { MovingObject::update(is, version); u8 buf[2]; // Read yaw * 10 is.read((char*)buf, 2); s16 yaw_i = readS16(buf); m_yaw = (f32)yaw_i / 10; updateNodePos(); } virtual bool serverStep(float dtime, u32 daynight_ratio) { // Player is handled elsewhere. // Die. //return true; // Actually, fail very loudly: assert(0); } #ifndef SERVER virtual void clientStep(float dtime) { MovingObject::simpleMove(dtime); updateNodePos(); } virtual void addToScene(scene::ISceneManager *smgr); virtual void removeFromScene() { if(m_node == NULL) return; m_node->remove(); m_node = NULL; } virtual void updateLight(u8 light_at_pos) { if(m_node == NULL) return; u8 li = decode_light(light_at_pos); video::SColor color(255,li,li,li); scene::IMesh *mesh = m_node->getMesh(); u16 mc = mesh->getMeshBufferCount(); for(u16 j=0; j<mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); u16 vc = buf->getVertexCount(); for(u16 i=0; i<vc; i++) { vertices[i].Color = color; } } } #endif /* Special methods */ void updateNodePos() { if(m_node == NULL) return; m_node->setPosition(getAbsoluteShowPos()); m_node->setRotation(v3f(0, -m_yaw+180, 0)); } protected: scene::IMeshSceneNode *m_node; float m_yaw; v3f m_oldpos; }; struct DistanceSortedObject { DistanceSortedObject(MapBlockObject *a_obj, f32 a_d) { obj = a_obj; d = a_d; } MapBlockObject *obj; f32 d; bool operator < (DistanceSortedObject &other) { return d < other.d; } }; class MapBlockObjectList { public: MapBlockObjectList(MapBlock *block); ~MapBlockObjectList(); // Writes the count, id, the type id and the parameters of all objects void serialize(std::ostream &os, u8 version); // Reads ids, type_ids and parameters. // Creates, updates and deletes objects. // If smgr!=NULL, new objects are added to the scene void update(std::istream &is, u8 version, scene::ISceneManager *smgr, u32 daynight_ratio); // Finds a new unique id s16 getFreeId() throw(ContainerFullException); /* Adds an object. Set id to -1 to have this set it to a suitable one. The block pointer member is set to this block. */ void add(MapBlockObject *object) throw(ContainerFullException, AlreadyExistsException); // Deletes and removes all objects void clear(); /* Removes an object. Ignores inexistent objects */ void remove(s16 id); /* References an object. The object will not be valid after step() or of course if it is removed. Grabbing the lock is recommended while processing. */ MapBlockObject * get(s16 id); // You'll want to grab this in a SharedPtr JMutexAutoLock * getLock() { return new JMutexAutoLock(m_mutex); } // Steps all objects and if server==true, removes those that // want to be removed void step(float dtime, bool server, u32 daynight_ratio); // Wraps an object that wants to move onto this block from an another // Returns true if wrapping was impossible bool wrapObject(MapBlockObject *object); // origin is relative to block void getObjects(v3f origin, f32 max_d, core::array<DistanceSortedObject> &dest); // Number of objects s32 getCount() { return m_objects.size(); } private: JMutex m_mutex; // Key is id core::map<s16, MapBlockObject*> m_objects; MapBlock *m_block; u32 m_last_update_daynight_ratio; }; #endif