• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

How to detect which items are cloned

Explain how item disapear

I have no idea, but im suspecting the item getting to much attributes which make it "weight" more than 64kib which is max at phpmyadmin. (pokeserver)

Item = Pokemons/pokeballs.
 
I think that is 8.065817517 x 10^67 = 52!
that is just with letters

with numbers and letters is 62!

idk
Um? I did calculate it wrong last time, but not thaat bad :P
50^10 = 10 letters, each is one of 50 possible (a-zA-Z) = 97.656.250.000.000.000
900.000 = possible numbers 100.000 - 999.999

97.656.250.000.000.000 * 900.000 = 8.7 * 10^22 = MUCH = 87.890.625.000.000.000.000.00
So serial can be one of 87.890.625.000.000.000.000.00 combinations PER SECOND.
Even if you add serial to maany items per second there is no chance to get same serial for 2 of them.
So no more MySQL queries in serial generator! :)

EDIT:
I have no idea, but im suspecting the item getting to much attributes which make it "weight" more than 64kib which is max at phpmyadmin. (pokeserver)

Item = Pokemons/pokeballs.
Change in mysql (by phpmyadmin) columns 'type of data' ( column `attributes` in `player_items`, `player_depotitems` and `tile_items`) from BLOB to LONGBLOB (or MEDIUMBLOB), for TFS it will be same, but no more problem with 64KB limit. I found that problem loong time ago when developers added `house_data` table, 64KB to store all house tiles+items wasn't enought.

http://dev.mysql.com/doc/refman/5.0/en/blob.html said:
BLOB - 65535 bytes - 64KB
MEDIUMBLOB - 16,777,215 bytes (2^24 - 1) - 16MB
LONGBLOB - 4G bytes (2^32 – 1) - 4GB
 
Last edited:
Thanks Gesior, i hope it helps. Since i think its the attributes. But who knows :/ That why i want logs.
 
Gesior, I used this script
LUA:
local lettersUsedToGenerateHash = "AaBbCcDdEeFfGgHhIiJjKkLlMmOoPpQqRrSsTtUuVvWwXxYyZz"
math.randomseed(os.time()) -- required!
function generateSerial()
	local newSerial = "!"
	for k = 1, 10 do
		local l = math.random(1, string.len(lettersUsedToGenerateHash))
		newSerial = newSerial .. string.sub(lettersUsedToGenerateHash, l, l)
	end
	local newSerialInt  = math.random(899999) + 100000 -- this will return always same lenght
	newSerial = newSerial .. "-" .. os.time() .. "-" .. newSerialInt
	-- length: ![10 letters]-[10 numbers (unix date)]-[6 numbers]
	-- length = 29 [always!]
	return newSerial
end

local function doPlayerAddDepotItems(cid, items, town)
	if (not isPlayer(cid)) then
		error("Player not found")
	end
	local town = town or getPlayerTown(cid)
 
	local attritemid = doCreateItemEx(2596, 1)
	for item, count in pairs(items) do
		if (type(item) == "number") then
			local thing = doAddContainerItem(attritemid, item, count)
			doItemSetAttribute(thing, "description", "This item belongs to " .. getPlayerName(cid) .. ".")
			doItemSetAttribute(thing, "owner", getPlayerGUID(cid))
			doItemSetAttribute(thing, "aid", getPlayerGUID(cid)+10000)
			doItemSetAttribute(thing, "serial", generateSerial())
		elseif (type(item) == "string") then
			if getItemIdByName(item) then
				local thing = doAddContainerItem(attritemid, item, count)
				doItemSetAttribute(thing, "description", "This item belongs to " .. getPlayerName(cid) .. ".")
				doItemSetAttribute(thing, "owner", getPlayerGUID(cid))
				doItemSetAttribute(thing, "aid", getPlayerGUID(cid)+10000)
				doItemSetAttribute(thing, "serial", generateSerial())
			end
		else
			error("Undefinied type of item name")
		end
	end
 
	return doPlayerSendMailByName(getCreatureName(cid), attritemid, town) 
end
 
function onThink(interval, lastExecution, thinkInterval)
 
	local result = db.getResult("SELECT * FROM shop_history WHERE `processed` = 0;")
 
		if(result:getID() ~= -1) then
			while(true) do
				cid = getCreatureByName(tostring(result:getDataString("player")))
				product = tonumber(result:getDataInt("product"))
				itemr = db.getResult("SELECT * FROM shop_offer WHERE `id` = "..product..";")
					if isPlayer(cid) then
						local id = tonumber(itemr:getDataInt("item"))
						local tid = tonumber(result:getDataInt("id"))
						local count = tonumber(itemr:getDataInt("count"))
						local tipe = tonumber(itemr:getDataInt("type"))
						local productn = tostring(itemr:getDataString("name"))
							if isInArray({5,8},tipe) then
								if isPlayer(cid) then
									local received = doPlayerAddDepotItems(cid, {[id]=count})
									if received then
										doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "You received "..productn.." in depot of your hometown.")
										db.executeQuery("UPDATE `shop_history` SET `processed`='1' WHERE id = " .. tid .. ";")
										doPlayerSave(cid)
									else
										doPlayerSendTextMessage(cid,19, "Report to a gamemaster..")
									end
								else
									doPlayerSendTextMessage(cid,19, "Report to a gamemaster.")
								end
							elseif isInArray({6,7},tipe) then
									if tipe == 6 then
										bcap = 8
										bid = 1987
									elseif tipe == 7 then
										bcap = 20
										bid = 1988
									end
									if isItemRune(id) then
										count = 1
									end
									if isPlayer(cid) then
										local bag = doCreateItemEx(bid, 1)
											for i = 1,bcap do
												doAddContainerItem(bag, id, count)
											end
										received = doPlayerAddItemEx(cid, bag, true)
										if received then
											doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "You received "..productn.." in depot of your hometown.")
											db.executeQuery("UPDATE `shop_history` SET `processed`='1' WHERE id = " .. tid .. ";")
											doPlayerSave(cid)
										else
											doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "You need a free slot on container to receive "..productn..".")
										end
									else
										doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_ORANGE, "You need "..getItemWeightById(id, count).." of free capacity to receive "..productn..".")
									end
							end
					end
				itemr:free()
				if not(result:next()) then
					break
				end
			end
			result:free()
		end
	return true
end

Player_items.sql
 
Last edited:
Lucas any about logs on disapearing items?
 
Ok :/ please msg me if you have idea.

- - - Updated - - -

Um? I did calculate it wrong last time, but not thaat bad :P
50^10 = 10 letters, each is one of 50 possible (a-zA-Z) = 97.656.250.000.000.000
900.000 = possible numbers 100.000 - 999.999

97.656.250.000.000.000 * 900.000 = 8.7 * 10^22 = MUCH = 87.890.625.000.000.000.000.00
So serial can be one of 87.890.625.000.000.000.000.00 combinations PER SECOND.
Even if you add serial to maany items per second there is no chance to get same serial for 2 of them.
So no more MySQL queries in serial generator! :)

EDIT:

Change in mysql (by phpmyadmin) columns 'type of data' ( column `attributes` in `player_items`, `player_depotitems` and `tile_items`) from BLOB to LONGBLOB (or MEDIUMBLOB), for TFS it will be same, but no more problem with 64KB limit. I found that problem loong time ago when developers added `house_data` table, 64KB to store all house tiles+items wasn't enought.

Gesior it didnt help, still getting reports :/
 
The problem for the script is read the serial exactly as it is from BLOB field with a query.

A common problem is that an item has more than one attribute (description, aid, uid, etc) it won't return the serial as it is.

So i've been thinking to create a new AntiDupe that requires c++
 
It will be perfect if we 'know' all vip itens then we just have to search for them (no big cost to cpu)
 
Select serials from items attributes that got more then one item:
PHP:
SELECT COUNT(1), substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS xx  FROM `player_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 GROUP BY xx HAVING COUNT(1) > 1
I must write now something else. I will post query to select duplicated serials from many tables in 1-2 hours. Then you just call second query to delete items with serials = X

EDIT:
Of course word 'serial' (in script that set 'serial' item attribute) should be changed to some unique text like 'ha34sdjcx' (that players cannot know). If you keep it 'serial' than players can somehow abuse it by editing text of label/letter and writing in it:
serial12345!here-fake-344-serial
(12345 - 5 bytes between attribute name and it's value)
but then you must edit query, because there is:
+11 which means length of attribute name (for 'serial' it's 6) + 5 (1 byte [attribute type: text or number] + 4 bytes [length of attribute], for text it's always 4 bytes)
More about reading attributes:
http://otland.net/f82/shop-lua-play...n-website-shop-127774/index3.html#post1298750
 
Last edited:
I've been working in a new AntiDupe that works in c++. I already made that items are saved/loaded with a serial that's stored in a new field at player_items/player_depotitems.
Code:
15:50 You see a solar axe (Atk:52, Def:29 +3).
It can only be wielded properly by players of level 130 or higher.
It weighs 110.00 oz.
ItemID: [8925].
[COLOR="#FF0000"]Serial: [E3Gh-PS4q9mgWzJ].[/COLOR]
Position: {x = 859, y = 964, z = 7}.
 
Last edited:
Query to load duplicated serial IDs, from all tables:
SELECT unitedItems.serial, COUNT(1) AS duplicatesCount FROM (SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `player_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0
UNION ALL
SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `player_depotitems` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0
UNION ALL
SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `tile_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0) unitedItems GROUP BY unitedItems.serial HAVING COUNT(1) > 1;


Returns list of serials that are in attributes of more then one item on server like:
antidupe.PNG


Then you must make a string (in LUA) of all these selected 'serials' and put them into query (in right place: '... IN (HERE)').
Then run query 3 times for 3 tables:
DELETE FROM `player_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');

DELETE FROM `player_depotitems` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');

DELETE FROM `tile_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');
 
This part is working fine. Now i'm going to work in queries to detect cloned items which do not f*ck the connection.

- - - Updated - - -

Query to load duplicated serial IDs, from all tables:
SELECT unitedItems.serial, COUNT(1) AS duplicatesCount FROM (SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `player_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0
UNION ALL
SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `player_depotitems` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0
UNION ALL
SELECT substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) AS serial FROM `tile_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0) unitedItems GROUP BY unitedItems.serial HAVING COUNT(1) > 1;


Returns list of serials that are in attributes of more then one item on server like:
antidupe.PNG


Then you must make a string (in LUA) of all these selected 'serials' and put them into query (in right place: '... IN (HERE)').
Then run query 3 times for 3 tables:
DELETE FROM `player_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');

DELETE FROM `player_depotitems` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');

DELETE FROM `tile_items` WHERE LOCATE('serial', CONVERT(`attributes` USING latin1)) != 0 AND substring(CONVERT(`attributes` USING latin1) , LOCATE('serial', CONVERT(`attributes` USING latin1))+11, 29) IN ('first selected serial', 'second selected serial', 'and more', 'more');

These queries won't f*ck the connection if there is like 3.000+ items in game?
 
Back
Top