mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-11-04 09:15:29 +01:00 
			
		
		
		
	Add /clearobjects
This commit is contained in:
		@@ -647,6 +647,92 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerEnvironment::clearAllObjects()
 | 
			
		||||
{
 | 
			
		||||
	infostream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
			<<"Removing all active objects"<<std::endl;
 | 
			
		||||
	core::list<u16> objects_to_remove;
 | 
			
		||||
	for(core::map<u16, ServerActiveObject*>::Iterator
 | 
			
		||||
			i = m_active_objects.getIterator();
 | 
			
		||||
			i.atEnd()==false; i++)
 | 
			
		||||
	{
 | 
			
		||||
		ServerActiveObject* obj = i.getNode()->getValue();
 | 
			
		||||
		u16 id = i.getNode()->getKey();		
 | 
			
		||||
		v3f objectpos = obj->getBasePosition();	
 | 
			
		||||
		// Delete static object if block is loaded
 | 
			
		||||
		if(obj->m_static_exists){
 | 
			
		||||
			MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
 | 
			
		||||
			if(block){
 | 
			
		||||
				block->m_static_objects.remove(id);
 | 
			
		||||
				block->raiseModified(MOD_STATE_WRITE_NEEDED);
 | 
			
		||||
				obj->m_static_exists = false;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// If known by some client, don't delete immediately
 | 
			
		||||
		if(obj->m_known_by_count > 0){
 | 
			
		||||
			obj->m_pending_deactivation = true;
 | 
			
		||||
			obj->m_removed = true;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		// Delete active object
 | 
			
		||||
		delete obj;
 | 
			
		||||
		// Id to be removed from m_active_objects
 | 
			
		||||
		objects_to_remove.push_back(id);
 | 
			
		||||
	}
 | 
			
		||||
	// Remove references from m_active_objects
 | 
			
		||||
	for(core::list<u16>::Iterator i = objects_to_remove.begin();
 | 
			
		||||
			i != objects_to_remove.end(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		m_active_objects.remove(*i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	core::list<v3s16> loadable_blocks;
 | 
			
		||||
	infostream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
			<<"Listing all loadable blocks"<<std::endl;
 | 
			
		||||
	m_map->listAllLoadableBlocks(loadable_blocks);
 | 
			
		||||
	infostream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
			<<"Done listing all loadable blocks: "
 | 
			
		||||
			<<loadable_blocks.size()
 | 
			
		||||
			<<", now clearing"<<std::endl;
 | 
			
		||||
	u32 report_interval = loadable_blocks.size() / 10;
 | 
			
		||||
	u32 num_blocks_checked = 0;
 | 
			
		||||
	u32 num_blocks_cleared = 0;
 | 
			
		||||
	u32 num_objs_cleared = 0;
 | 
			
		||||
	for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
 | 
			
		||||
			i != loadable_blocks.end(); i++)
 | 
			
		||||
	{
 | 
			
		||||
		v3s16 p = *i;
 | 
			
		||||
		MapBlock *block = m_map->emergeBlock(p, false);
 | 
			
		||||
		if(!block){
 | 
			
		||||
			errorstream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
					<<"Failed to emerge block "<<PP(p)<<std::endl;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		u32 num_stored = block->m_static_objects.m_stored.size();
 | 
			
		||||
		u32 num_active = block->m_static_objects.m_active.size();
 | 
			
		||||
		if(num_stored != 0 || num_active != 0){
 | 
			
		||||
			block->m_static_objects.m_stored.clear();
 | 
			
		||||
			block->m_static_objects.m_active.clear();
 | 
			
		||||
			block->raiseModified(MOD_STATE_WRITE_NEEDED);
 | 
			
		||||
			num_objs_cleared += num_stored + num_active;
 | 
			
		||||
			num_blocks_cleared++;
 | 
			
		||||
		}
 | 
			
		||||
		num_blocks_checked++;
 | 
			
		||||
 | 
			
		||||
		if(num_blocks_checked % report_interval == 0){
 | 
			
		||||
			float percent = 100.0 * (float)num_blocks_checked /
 | 
			
		||||
					loadable_blocks.size();
 | 
			
		||||
			infostream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
					<<"Cleared "<<num_objs_cleared<<" objects"
 | 
			
		||||
					<<" in "<<num_blocks_cleared<<" blocks ("
 | 
			
		||||
					<<percent<<"%)"<<std::endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	infostream<<"ServerEnvironment::clearAllObjects(): "
 | 
			
		||||
			<<"Finished: Cleared "<<num_objs_cleared<<" objects"
 | 
			
		||||
			<<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void getMob_dungeon_master(Settings &properties)
 | 
			
		||||
{
 | 
			
		||||
	properties.set("looks", "dungeon_master");
 | 
			
		||||
 
 | 
			
		||||
@@ -218,6 +218,11 @@ public:
 | 
			
		||||
 | 
			
		||||
	void addActiveBlockModifier(ActiveBlockModifier *abm);
 | 
			
		||||
 | 
			
		||||
	/* Other stuff */
 | 
			
		||||
	
 | 
			
		||||
	// Clear all objects, loading and going through every MapBlock
 | 
			
		||||
	void clearAllObjects();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										198
									
								
								src/map.cpp
									
									
									
									
									
								
							
							
						
						
									
										198
									
								
								src/map.cpp
									
									
									
									
									
								
							@@ -2623,152 +2623,6 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool allow_generate)
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	/*
 | 
			
		||||
		Do not generate over-limit
 | 
			
		||||
	*/
 | 
			
		||||
	if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 | 
			
		||||
	|| p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 | 
			
		||||
	|| p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 | 
			
		||||
	|| p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 | 
			
		||||
	|| p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
 | 
			
		||||
	|| p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
 | 
			
		||||
		throw InvalidPositionException("emergeBlock(): pos. over limit");
 | 
			
		||||
	
 | 
			
		||||
	v2s16 p2d(p.X, p.Z);
 | 
			
		||||
	s16 block_y = p.Y;
 | 
			
		||||
	/*
 | 
			
		||||
		This will create or load a sector if not found in memory.
 | 
			
		||||
		If block exists on disk, it will be loaded.
 | 
			
		||||
	*/
 | 
			
		||||
	ServerMapSector *sector;
 | 
			
		||||
	try{
 | 
			
		||||
		sector = createSector(p2d);
 | 
			
		||||
		//sector = emergeSector(p2d, changed_blocks);
 | 
			
		||||
	}
 | 
			
		||||
	catch(InvalidPositionException &e)
 | 
			
		||||
	{
 | 
			
		||||
		infostream<<"emergeBlock: createSector() failed: "
 | 
			
		||||
				<<e.what()<<std::endl;
 | 
			
		||||
		infostream<<"Path to failed sector: "<<getSectorDir(p2d)
 | 
			
		||||
				<<std::endl
 | 
			
		||||
				<<"You could try to delete it."<<std::endl;
 | 
			
		||||
		throw e;
 | 
			
		||||
	}
 | 
			
		||||
	catch(VersionMismatchException &e)
 | 
			
		||||
	{
 | 
			
		||||
		infostream<<"emergeBlock: createSector() failed: "
 | 
			
		||||
				<<e.what()<<std::endl;
 | 
			
		||||
		infostream<<"Path to failed sector: "<<getSectorDir(p2d)
 | 
			
		||||
				<<std::endl
 | 
			
		||||
				<<"You could try to delete it."<<std::endl;
 | 
			
		||||
		throw e;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		Try to get a block from the sector
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	bool does_not_exist = false;
 | 
			
		||||
	bool lighting_expired = false;
 | 
			
		||||
	MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
 | 
			
		||||
	
 | 
			
		||||
	// If not found, try loading from disk
 | 
			
		||||
	if(block == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		block = loadBlock(p);
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	// Handle result
 | 
			
		||||
	if(block == NULL)
 | 
			
		||||
	{
 | 
			
		||||
		does_not_exist = true;
 | 
			
		||||
	}
 | 
			
		||||
	else if(block->isDummy() == true)
 | 
			
		||||
	{
 | 
			
		||||
		does_not_exist = true;
 | 
			
		||||
	}
 | 
			
		||||
	else if(block->getLightingExpired())
 | 
			
		||||
	{
 | 
			
		||||
		lighting_expired = true;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// Valid block
 | 
			
		||||
		//infostream<<"emergeBlock(): Returning already valid block"<<std::endl;
 | 
			
		||||
		return block;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	/*
 | 
			
		||||
		If block was not found on disk and not going to generate a
 | 
			
		||||
		new one, make sure there is a dummy block in place.
 | 
			
		||||
	*/
 | 
			
		||||
	if(only_from_disk && (does_not_exist || lighting_expired))
 | 
			
		||||
	{
 | 
			
		||||
		//infostream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
 | 
			
		||||
 | 
			
		||||
		if(block == NULL)
 | 
			
		||||
		{
 | 
			
		||||
			// Create dummy block
 | 
			
		||||
			block = new MapBlock(this, p, true);
 | 
			
		||||
 | 
			
		||||
			// Add block to sector
 | 
			
		||||
			sector->insertBlock(block);
 | 
			
		||||
		}
 | 
			
		||||
		// Done.
 | 
			
		||||
		return block;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//infostream<<"Not found on disk, generating."<<std::endl;
 | 
			
		||||
	// 0ms
 | 
			
		||||
	//TimeTaker("emergeBlock() generate");
 | 
			
		||||
 | 
			
		||||
	//infostream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		If the block doesn't exist, generate the block.
 | 
			
		||||
	*/
 | 
			
		||||
	if(does_not_exist)
 | 
			
		||||
	{
 | 
			
		||||
		block = generateBlock(p, block, sector, changed_blocks,
 | 
			
		||||
				lighting_invalidated_blocks); 
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if(lighting_expired)
 | 
			
		||||
	{
 | 
			
		||||
		lighting_invalidated_blocks.insert(p, block);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	/*
 | 
			
		||||
		Initially update sunlight
 | 
			
		||||
	*/
 | 
			
		||||
	{
 | 
			
		||||
		core::map<v3s16, bool> light_sources;
 | 
			
		||||
		bool black_air_left = false;
 | 
			
		||||
		bool bottom_invalid =
 | 
			
		||||
				block->propagateSunlight(light_sources, true,
 | 
			
		||||
				&black_air_left);
 | 
			
		||||
 | 
			
		||||
		// If sunlight didn't reach everywhere and part of block is
 | 
			
		||||
		// above ground, lighting has to be properly updated
 | 
			
		||||
		//if(black_air_left && some_part_underground)
 | 
			
		||||
		if(black_air_left)
 | 
			
		||||
		{
 | 
			
		||||
			lighting_invalidated_blocks[block->getPos()] = block;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if(bottom_invalid)
 | 
			
		||||
		{
 | 
			
		||||
			lighting_invalidated_blocks[block->getPos()] = block;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
	
 | 
			
		||||
	return block;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
s16 ServerMap::findGroundLevel(v2s16 p2d)
 | 
			
		||||
{
 | 
			
		||||
#if 0
 | 
			
		||||
@@ -2867,6 +2721,12 @@ void ServerMap::verifyDatabase() {
 | 
			
		||||
			throw FileNotGoodException("Cannot prepare write statement");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
 | 
			
		||||
		if(d != SQLITE_OK) {
 | 
			
		||||
			infostream<<"WARNING: Database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
 | 
			
		||||
			throw FileNotGoodException("Cannot prepare read statement");
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		infostream<<"Server: Database opened"<<std::endl;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -3039,6 +2899,52 @@ void ServerMap::save(bool only_changed)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static s32 unsignedToSigned(s32 i, s32 max_positive)
 | 
			
		||||
{
 | 
			
		||||
	if(i < max_positive)
 | 
			
		||||
		return i;
 | 
			
		||||
	else
 | 
			
		||||
		return i - 2*max_positive;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// modulo of a negative number does not work consistently in C
 | 
			
		||||
static sqlite3_int64 pythonmodulo(sqlite3_int64 i, sqlite3_int64 mod)
 | 
			
		||||
{
 | 
			
		||||
	if(i >= 0)
 | 
			
		||||
		return i % mod;
 | 
			
		||||
	return mod - ((-i) % mod);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i)
 | 
			
		||||
{
 | 
			
		||||
	s32 x = unsignedToSigned(pythonmodulo(i, 4096), 2048);
 | 
			
		||||
	i = (i - x) / 4096;
 | 
			
		||||
	s32 y = unsignedToSigned(pythonmodulo(i, 4096), 2048);
 | 
			
		||||
	i = (i - y) / 4096;
 | 
			
		||||
	s32 z = unsignedToSigned(pythonmodulo(i, 4096), 2048);
 | 
			
		||||
	return v3s16(x,y,z);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst)
 | 
			
		||||
{
 | 
			
		||||
	if(loadFromFolders()){
 | 
			
		||||
		errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
 | 
			
		||||
				<<"all blocks that are stored in flat files"<<std::endl;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	{
 | 
			
		||||
		verifyDatabase();
 | 
			
		||||
		
 | 
			
		||||
		while(sqlite3_step(m_database_list) == SQLITE_ROW)
 | 
			
		||||
		{
 | 
			
		||||
			sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
 | 
			
		||||
			v3s16 p = getIntegerAsBlock(block_i);
 | 
			
		||||
			//dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
 | 
			
		||||
			dst.push_back(p);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerMap::saveMapMeta()
 | 
			
		||||
{
 | 
			
		||||
	DSTACK(__FUNCTION_NAME);
 | 
			
		||||
 
 | 
			
		||||
@@ -383,6 +383,7 @@ public:
 | 
			
		||||
	void verifyDatabase();
 | 
			
		||||
	// Get an integer suitable for a block
 | 
			
		||||
	static sqlite3_int64 getBlockAsInteger(const v3s16 pos);
 | 
			
		||||
	static v3s16 getIntegerAsBlock(sqlite3_int64 i);
 | 
			
		||||
 | 
			
		||||
	// Returns true if the database file does not exist
 | 
			
		||||
	bool loadFromFolders();
 | 
			
		||||
@@ -394,6 +395,8 @@ public:
 | 
			
		||||
	void save(bool only_changed);
 | 
			
		||||
	//void loadAll();
 | 
			
		||||
	
 | 
			
		||||
	void listAllLoadableBlocks(core::list<v3s16> &dst);
 | 
			
		||||
	
 | 
			
		||||
	// Saves map seed and possibly other stuff
 | 
			
		||||
	void saveMapMeta();
 | 
			
		||||
	void loadMapMeta();
 | 
			
		||||
@@ -458,6 +461,7 @@ private:
 | 
			
		||||
	sqlite3 *m_database;
 | 
			
		||||
	sqlite3_stmt *m_database_read;
 | 
			
		||||
	sqlite3_stmt *m_database_write;
 | 
			
		||||
	sqlite3_stmt *m_database_list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 
 | 
			
		||||
@@ -4136,6 +4136,11 @@ void Server::notifyPlayer(const char *name, const std::wstring msg)
 | 
			
		||||
	SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Server::notifyPlayers(const std::wstring msg)
 | 
			
		||||
{
 | 
			
		||||
	BroadcastChatMessage(msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
v3f findSpawnPos(ServerMap &map)
 | 
			
		||||
{
 | 
			
		||||
	//return v3f(50,50,50)*BS;
 | 
			
		||||
 
 | 
			
		||||
@@ -475,6 +475,7 @@ public:
 | 
			
		||||
	
 | 
			
		||||
	// Envlock and conlock should be locked when calling this
 | 
			
		||||
	void notifyPlayer(const char *name, const std::wstring msg);
 | 
			
		||||
	void notifyPlayers(const std::wstring msg);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -277,6 +277,35 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_clearobjects(std::wostringstream &os,
 | 
			
		||||
	ServerCommandContext *ctx)
 | 
			
		||||
{
 | 
			
		||||
	if((ctx->privs & PRIV_SERVER) ==0)
 | 
			
		||||
	{
 | 
			
		||||
		os<<L"-!- You don't have permission to do that";
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	actionstream<<ctx->player->getName()
 | 
			
		||||
			<<" clears all objects"<<std::endl;
 | 
			
		||||
	
 | 
			
		||||
	{
 | 
			
		||||
		std::wstring msg;
 | 
			
		||||
		msg += L"Clearing all objects. This may take long.";
 | 
			
		||||
		msg += L" You may experience a timeout. (by ";
 | 
			
		||||
		msg += narrow_to_wide(ctx->player->getName());
 | 
			
		||||
		msg += L")";
 | 
			
		||||
		ctx->server->notifyPlayers(msg);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx->env->clearAllObjects();
 | 
			
		||||
					
 | 
			
		||||
	actionstream<<"object clearing done"<<std::endl;
 | 
			
		||||
	
 | 
			
		||||
	os<<L"*** cleared all objects";
 | 
			
		||||
	ctx->flags |= SEND_TO_OTHERS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
std::wstring processServerCommand(ServerCommandContext *ctx)
 | 
			
		||||
{
 | 
			
		||||
@@ -302,45 +331,28 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
 | 
			
		||||
			os<<L" ban unban";
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"status")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_status(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"privs")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_privs(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_grantrevoke(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"time")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_time(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"shutdown")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_shutdown(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"setting")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_setting(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"teleport")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_teleport(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_banunban(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"me")
 | 
			
		||||
	{
 | 
			
		||||
		cmd_me(os, ctx);
 | 
			
		||||
	}
 | 
			
		||||
	else if(ctx->parms[0] == L"clearobjects")
 | 
			
		||||
		cmd_clearobjects(os, ctx);
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		os<<L"-!- Invalid command: " + ctx->parms[0];
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	return os.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user