Need a hand decoding some code i have to maintain.

Place to get help with not working mods / modding interface.
EurypteriD
Inserter
Inserter
Posts: 49
Joined: Wed Jun 04, 2014 11:00 am
Contact:

Need a hand decoding some code i have to maintain.

Post by EurypteriD »

With the approval from The original mod owner i have started upgrading a mod. i am about 92% done adding all the new items but the Control.Lua side of this is giving me a headache.

If there is someoene in EU time that could on a skype with me help me break this down and add comments so it is easy to see what does what. then please give me a shout.
Credits will be given in the finalized release for 12.20-21 when i am done both in the mod files and on the youtube video showing it off.

Please send me a PM if you are interested.
Thanks
Let's Play Youtube Channel
User avatar
Klonan
Factorio Staff
Factorio Staff
Posts: 5409
Joined: Sun Jan 11, 2015 2:09 pm
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by Klonan »

Its best if you just post the code here so we can help
EurypteriD
Inserter
Inserter
Posts: 49
Joined: Wed Jun 04, 2014 11:00 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by EurypteriD »

Code: Select all

local north = defines.direction.north
local east = defines.direction.east
local south = defines.direction.south
local west = defines.direction.west

remote.add_interface("sspl",
{
	clean = function()
		global.sspl = {	splitSets = {},
						splitSetsToRemove = {},
						version = utils.currentVersion
				   }
	end
})

script.on_init(function() OnInit() end)
script.on_load(function() OnLoad() end)
script.on_event(defines.events.on_tick, function(event) OnTick(event) end)
script.on_event(defines.events.on_built_entity, function(event) OnBuiltEntity(event) end)
script.on_event(defines.events.on_entity_died, function(event) OnEntityDied(event) end)
script.on_event(defines.events.on_preplayer_mined_item, function(event) OnPrePlayerMinedItem(event) end)


function OnInit()
	--game.player.print("SmartSplitters: OnInit")
	if global.sspl == nil then
		global.sspl = {	splitSets = {},
						splitSetsToRemove = {},
						version = utils.currentVersion
				   }
	else
		local total = 0
		for _, set in pairs(global.sspl.splitSets) do
			total = total + #set.splitters
		end
		
		--game.player.print("Loaded " .. #global.sspl.splitSets .. " splitter sets containing " .. total .. " splitters.")
	end
	if not global.sspl.version then
		game.player.print("Migrating SmartSplitters from unversioned to version " .. utils.currentVersion)
		utils.migrations.versionless()
		game.player.print("Migration successful.")
	end
	if global.sspl.version ~= utils.currentVersion then
		game.player.print("Migrating SmartSplitters from version " .. global.sspl.version .. " to version " .. utils.currentVersion .. ".")
		utils.migrations[global.sspl.version]()
		game.player.print("Migration successful.")
	end
	
--	if DF_MakeItems then
--	 startingItems()
--	end
	
end

function OnLoad()
	--game.player.print("SmartSplitters: OnLoad")
	-- Call OnInit to ensure global.teles exists.
	OnInit()
end

function OnTick(_Event)
	--game.player.print("SmartSplitters: OnTick")
	-- Check if there's any sets with invalid splitters and remove those from the splitter lists.
	-- Then check if there's any sets with zero splitters and remove those from the global set table.
	if game.tick % 60 == 0 then
		local toRemove = {}
		for setID, set in pairs(global.sspl.splitSets) do
			splitterToRemove = {}
			for splitterID, splitter in pairs(set.splitters) do
				if not splitter.valid then
					table.insert(splitterToRemove, splitterID)
				end
			end
			for _, splitterID in pairs(splitterToRemove) do
				table.remove(set.splitters, splitterID)
			end
			if #set.splitters < 1 then
				table.insert(toRemove, setID)
			end
		end
		for _, setID in pairs(toRemove) do
			table.remove(global.sspl.splitSets)
		end
	end
	
	-- Check for new filters 6 times per second.
	if game.tick % 10 == 0 then
		for set_no, set in pairs(global.sspl.splitSets) do
			RecalculateSet(set)
--			Debug_DumpSet(set_no)
		end
	end
	
	local facing_directions = {[north] = south, [east] = west, [south] = north, [west] = east}
	
	if game.tick % 5 == 0 then
  	for set_no, set in pairs(global.sspl.splitSets) do
  		for sp_no, splitter in pairs(set.splitters) do
  			if splitter.valid then
  				local scanArea = GetScanArea(splitter.direction, splitter.position)
          local belt=splitter.surface.find_entities_filtered{type = "transport-belt", area = scanArea}
          
          if belt[1] ~= nil   and belt[1].direction == splitter.direction then
  --          debugLog("nway " ..  tostring(belt[1].name).." - ".. tostring(belt[1].type) .." - ".. tostring(belt[1].direction).." : " ..tostring(splitter.direction).." : " ..tostring(facing_directions[splitter.direction]) )
            for laneI=1,2,1 do
              local lane=belt[1].get_transport_line(laneI)
              local cnt=lane.get_item_count()
              local cmap=lane.get_contents()
              if cnt ~= 0 then
                for res_name,res_count in pairs(cmap) do
                  
                  local targetSplitterSet = nil
                  local targetIsNonFilter=false
                  if set.filteredItems[res_name] then
                    targetSplitterSet = set.filteredItems[res_name]
                  else
                    targetSplitterSet = set.nonfilteringSplitters
                    targetIsNonFilter=true
                  end
  
                  if not set.itemCounts[res_name] then
                    set.itemCounts[res_name] = 0
                  else
                    set.itemCounts[res_name] = set.itemCounts[res_name] + 1
                  end
  
                  local moved = false
                  local tries = 0
                  local curr_idx=0
                  while not moved and tries < #targetSplitterSet do
                    local li=((set.itemCounts[res_name] + tries) % #targetSplitterSet)
                    curr_idx=li
                    local targetSplitter = targetSplitterSet[li + 1]
                    
                    moved = MoveStack12(res_name, targetSplitter, lane,laneI)
                    tries = tries + 1
                  end
                  if ( moved == false ) and ( targetIsNonFilter == true ) and (DF_PrintNonMoved == 1) then
                    Debug_DumpSet(set_no)
                    debugLog(string.format("nws not moved %s %d.%d:%d to %s.%d:%d ",res_name,set_no,sp_no,laneI,table.tostring(targetSplitterSet),curr_idx,laneI))                    
                  end
--                  if moved then
--                    debugLog(string.format("nws move %s %d.%d:%d to %s.%d:%d ",res_name,set_no,sp_no,laneI,tostring(targetSplitterSet[curr_idx + 1]),curr_idx,laneI))--..table.tostring(targetSplitterSet)
--                  end
                  
                end
              end
              
            end
          end
  		
  			end
  		end
  	end
  end
  
end

local directionToOffset = {[0] = {x = -1, y =  0},
						   [2] = {x =  0, y = -1},
						   [4] = {x =  1, y =  0},
						   [6] = {x =  0, y =  1}}
						   
function OnBuiltEntity(_Event)
	local _Entity = _Event.created_entity
	if IsSmartSplitter(_Entity) then
		--game.player.print("Built smartsplitter")
		local _Position = _Entity.position
		local _Direction = _Entity.direction
		local _Surface=_Entity.surface
		
		-- Check if there's a splitter to the left.
		local scanPosl = {x = _Position.x + directionToOffset[_Direction].x, y = _Position.y + directionToOffset[_Direction].y}
		local scanAreal = {{scanPosl.x - 0.25, scanPosl.y - 0.25}, {scanPosl.x + 0.25, scanPosl.y + 0.25}}
		local scanl = _Surface.find_entities_filtered{name = "smartsplitter", area = scanAreal}
		
		-- Check if there's a splitter to the right.
		local scanPosr = {x = _Position.x - directionToOffset[_Direction].x, y = _Position.y - directionToOffset[_Direction].y}
		local scanArear = {{scanPosr.x - 0.25, scanPosr.y - 0.25}, {scanPosr.x + 0.25, scanPosr.y + 0.25}}
		local scanr = _Surface.find_entities_filtered{name = "smartsplitter", area = scanArear}
		
		if DF_DrawBuildMarkers == 1 then
  		local facing_directions = {[north] = south, [east] = west, [south] = north, [west] = east}
  		local takeArea = GetScanArea(_Direction, _Position)
      local dropArea = GetScanArea(facing_directions[_Direction], _Position)
      _Surface.create_entity{name="m2k-dbg-small-red", position = takeArea[1], force = game.forces.player}
      _Surface.create_entity{name="m2k-dbg-small-blue", position = takeArea[2], force = game.forces.player}
      _Surface.create_entity{name="m2k-dbg-small-green", position = dropArea[1], force = game.forces.player}
      _Surface.create_entity{name="m2k-dbg-small-yellow", position = dropArea[2], force = game.forces.player}
    end
    
		local lset = nil
		local rset = nil
		
		local lsetID = nil
		if #scanl == 1 and scanl[1].direction == _Direction then
			lsetID = GetAppropriateSetID(scanl[1])
		end
		local rsetID = nil
		if #scanr == 1 and scanr[1].direction == _Direction then
			rsetID = GetAppropriateSetID(scanr[1])
		end
		
		local set = CreateNewSet()
		if lsetID and rsetID then
			-- Add all splitters from the left set into the new set.
			for _, v in pairs(global.sspl.splitSets[lsetID].splitters) do
				table.insert(set.splitters, v)
			end
			global.sspl.splitSets[lsetID].splitters = {}
			-- Add the current splitter.
			table.insert(set.splitters, _Entity)
			-- Add all splitters from the right set.
			for _, v in pairs(global.sspl.splitSets[rsetID].splitters) do
				table.insert(set.splitters, v)
			end
			global.sspl.splitSets[rsetID].splitters = {}
			-- Remove left and right set.
			-- Remove the higher ID first, as the other ID won't be correct otherwise.
			if lsetID > rsetID then
				--game.player.print("Removing left set.")
				RemoveSetByID(lsetID)
				--game.player.print("Removing right set.")
				RemoveSetByID(rsetID)
			else
				--game.player.print("Removing right set.")
				RemoveSetByID(rsetID)
				--game.player.print("Removing left set.")
				RemoveSetByID(lsetID)
			end
			AddSet(set)
			--game.player.print("Merged sets.")
		elseif lsetID then
			--game.player.print("Added to left set.")
			set = global.sspl.splitSets[lsetID]
			-- Add the current splitter to the end of the set.
			table.insert(set.splitters, _Entity)
		elseif rsetID then
			--game.player.print("Added to right set.")
			set = global.sspl.splitSets[rsetID]
			-- Add the current splitter to the start of the set.
			table.insert(set.splitters, 1, _Entity)
		else
			--game.player.print("Created new set.")
			-- Add the splitter to the new set.
			table.insert(set.splitters, _Entity)
			AddSet(set)
		end
		
		RecalculateSet(set)
	end
end

function OnEntityDied(_Event)
	if IsSmartSplitter(_Event.entity) then
		RemoveSmartSplitter(_Event.entity)
	end
end

function OnPrePlayerMinedItem(_Event)
	if IsSmartSplitter(_Event.entity) then
		RemoveSmartSplitter(_Event.entity)
	end
end

function IsSmartSplitter(_Entity)
	return (_Entity.name == "smartsplitter")
end

function CreateNewSet()
	return {	splitters = {},
				filteredItems = {},
				itemCounts = {},
				filteringSplitters = {},
				nonfilteringSplitters = {}
		   }
end

function RemoveSmartSplitter(_Splitter)
	--game.player.print("RemoveSmartSplitter on splitter at [" .. _Splitter.position.x .. ", " .. _Splitter.position.y .. "]")
	local setID, splitterI = GetAppropriateSetID(_Splitter)
	
	if setID then
		local set = global.sspl.splitSets[setID]
		-- If the splitter is at the edge.
		if splitterI == 1 or splitterI == #set.splitters then
			table.remove(set.splitters, splitterI)
			RecalculateSet(set)
		else
			--game.player.print("Splitting set into 2.")
			-- Splitter is somewhere in the middle, split the set.
			leftSet = CreateNewSet()
			rightSet = CreateNewSet()
			
			for i = 1, splitterI - 1 do
				table.insert(leftSet.splitters, set.splitters[i])
			end
			for i = splitterI + 1, #set.splitters do
				table.insert(rightSet.splitters, set.splitters[i])
			end
			set.splitters = {}
			
			--game.player.print("Removing old set.")
			RemoveSetByID(setID)
			
			AddSet(leftSet)
			RecalculateSet(leftSet)
			
			AddSet(rightSet)
			RecalculateSet(rightSet)
		end
	end
end

function AddSet(_Set)
	table.insert(global.sspl.splitSets, _Set)
	
--  debugLog("Added set")
--  debugLog(#global.sspl.splitSets .. " sets")

end

function RemoveSet(_Set)
	-- Not yet tested
	local id = 0
	for i, set in pairs(global.sspl.splitSets) do
		if set == _Set then
			id = i
			break;
		end
	end
	if id ~= 0 then
		if #global.sspl.splitSets[id].splitters > 0 then
      debugLog("Removing split set with nonzero splitters.")
		end
		table.remove(global.sspl.splitSets, id)
	else
		--game.player.print("Tried to remove nonexistent set.")
	end
--    debugLog("Removed set " .. id)
--    debugLog(#global.sspl.splitSets .. " sets")
end

function RemoveSetByID(_ID)
	if #global.sspl.splitSets[_ID].splitters > 0 then
		debugLog("Removing split set with nonzero splitters.")
	end
	table.remove(global.sspl.splitSets, _ID)
--	debugLog("Removed set " .. _ID)
--	debugLog(#global.sspl.splitSets .. " sets")
end

function RecalculateSet(_Set)
	_Set.nonfilteringSplitters = {}
	_Set.filteredItems = {}
	_Set.filteringSplitters = {}
	
	if #_Set.splitters < 1 then
		--game.player.print("Empty set, removing.")
		RemoveSet(_Set)
	end
	
	for splitterID, splitter in pairs(_Set.splitters) do
		if splitter.valid then
			-- Fill item filter table.
			for i = 1, 5 do
				local itemName = splitter.get_filter(i)
				if itemName then
					table.insert(_Set.filteringSplitters, splitterID, splitter)
					if _Set.filteredItems[itemName] then
						table.insert(_Set.filteredItems[itemName], splitter)
					else
						_Set.filteredItems[itemName] = {[1] = splitter}
					end
				end
			end
			if not _Set.filteringSplitters[splitterID] then
				table.insert(_Set.nonfilteringSplitters, splitter)
			end
		end
	end
end

function GetAppropriateSetID(_Entity)
	for setID, set in pairs(global.sspl.splitSets) do
		for splitterI, splitter in pairs(set.splitters) do
			if splitter == _Entity then
				return setID, splitterI
			end
		end
	end
	game.player.print("ERROR: No set ID found for smartsplitter at [" .. _Entity.position.x .. ", " .. _Entity.position.y .. "]")
	--game.player.print("This should not happen, so please tell the mod author (ThaPear) what you were doing when this happened.")
	--game.player.print("This means: Were you merging 2 rows of splitters, building it left/right/above/below other splitters, in what direction were those splitters?")
	return nil, nil
end



function GetScanArea(_Direction, _Position)
  local beltscan_coords = { -- Points to search for transport belts.
    [north] = {{_Position.x - 0.3, _Position.y - 1.1},{_Position.x + 0.3, _Position.y - 0.8}},
    [east] =  {{_Position.x + 1.1  , _Position.y - 0.3},{_Position.x + 0.8, _Position.y + 0.3}},
    [south] = {{_Position.x - 0.3, _Position.y + 1.1},{_Position.x + 0.3, _Position.y + 0.8}},
    [west] =  {{_Position.x - 1.1  , _Position.y - 0.3},{_Position.x - 0.8, _Position.y + 0.3}}
  }

    if _Direction == north then
      return beltscan_coords[south]
  	elseif _Direction == east then
  	 return beltscan_coords[west]
  	elseif _Direction == south then
  	 return beltscan_coords[north]
  	elseif _Direction == west then
  	 return beltscan_coords[east]
  	else
  	 return {{pos.x , pos.y},{pos.x , pos.y}}
  	end
end

function MoveStack12(_ItemName, _Splitter, _SrcLine, _LineI)
  if _Splitter.energy < 1 then
    return false
  end
  
  local facing_directions = {[north] = south, [east] = west, [south] = north, [west] = east}
  local scanArea = GetScanArea(facing_directions[_Splitter.direction], _Splitter.position)

--[[   
  local found=false
  for i = 1, 5 do
    local itemName = _Splitter.get_filter(i)
    if itemName and itemName == _ItemName then
      found=true
    end
  end
  
  if not found then
    return
  end
--]]
    
  local belt=_Splitter.surface.find_entities_filtered{type = "transport-belt", area = scanArea}
  if belt[1] ~= nil and belt[1].direction == _Splitter.direction then
--    debugLog(string.format("nws ms12 dir=%s wd=%s pos=%s sa=%s bp=%s",_Splitter.direction,facing_directions[_Splitter.direction],table.tostring(_Splitter.position),table.tostring(scanArea),table.tostring(belt[1].position)))
    -- Check if the target location is empty.
    local tgtLane=belt[1].get_transport_line(_LineI)
    if tgtLane.can_insert_at_back() then
      local stack = {name = _ItemName, count = 1}
      _SrcLine.remove_item (stack)
      tgtLane.insert_at_back(stack)
      return true
    end
    return false
  end
end
Let's Play Youtube Channel
User avatar
rk84
Filter Inserter
Filter Inserter
Posts: 556
Joined: Wed Feb 13, 2013 9:15 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by rk84 »

Could you also tell if it gives you any error messages or what the code is not doing the way it should?
At first glance, I see "game" is used in OnInit function and that function is called on load. game is nil on load.
Test mode
Searching Flashlight
[WIP]Fluid handling expansion
[WIP]PvP gamescript
[WIP]Rocket Express
Autofill: The torch has been pass to Nexela
EurypteriD
Inserter
Inserter
Posts: 49
Joined: Wed Jun 04, 2014 11:00 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by EurypteriD »

Well this code is what making a splitter being able to filter what goes trhough it and outputs it to the correct lane.
Including detection if there is 1-xx splitters in a row. (each splitter is 1 tile big)

If i undo all i have done. It will work fine.
But the idea is that im adding 3 tiers of this. They now got a new name instead of smartsplitter each tier is named after the tier of belt. So basicsmartsplitter and so on.
Let's Play Youtube Channel
User avatar
rk84
Filter Inserter
Filter Inserter
Posts: 556
Joined: Wed Feb 13, 2013 9:15 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by rk84 »

There is at least couple things that need to be alter to work with new names.

I suggest you add list of new splitter names to begin of control.lua
e.g.

Code: Select all

local IsSmartSplitter= {
  "basicsmartsplitter" = true,
  "bettersmartsplitter" = true,
  "bestsmartsplitter" = true
}
Now IsSmartSplitter function can be removed and it calls can be replace with above name list.
change lines 167, 256 and 262

Code: Select all

if IsSmartSplitter[_Entity.name] then
lastly find_entities_filtered can be changed to find all splitters with type value. and again the namelist is used to tell smart ones.
lines 176 and 181

Code: Select all

local scanl = _Surface.find_entities_filtered{type= "splitter", area = scanAreal}
if scanl[1] and not IsSmartSplitter[scanl[1].name] then
  scanl[1] = nil
end
--
local scanr = _Surface.find_entities_filtered{type= "splitter", area = scanArear}
if scanr[1] and not IsSmartSplitter[scanr[1].name] then
  scanr[1] = nil
end
I hope this helps.
Test mode
Searching Flashlight
[WIP]Fluid handling expansion
[WIP]PvP gamescript
[WIP]Rocket Express
Autofill: The torch has been pass to Nexela
EurypteriD
Inserter
Inserter
Posts: 49
Joined: Wed Jun 04, 2014 11:00 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by EurypteriD »

Thanks RK84.

That is actually very good suggestions.. Only thing i need now is..
Where in this code does it state the speed of the splitter.
As i know and tested it deals with about 60 items a sec. and i want to make the lower tier of them Slower.

Would it be easiere for me to add in in the section where it scans that the input belt have to be ie basic belt or so?
Let's Play Youtube Channel
User avatar
rk84
Filter Inserter
Filter Inserter
Posts: 556
Joined: Wed Feb 13, 2013 9:15 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by rk84 »

EurypteriD wrote:Thanks RK84.

That is actually very good suggestions.. Only thing i need now is..
Where in this code does it state the speed of the splitter.
As i know and tested it deals with about 60 items a sec. and i want to make the lower tier of them Slower.

Would it be easiere for me to add in in the section where it scans that the input belt have to be ie basic belt or so?
To be honest, I have no experience of transport lines or this mod. But the code seems to work with transport lines 12 times in second. Inside "if game.tick % 5 == 0 then" -block is MoveStack12 -function that moves 1 item per call.

You could star by experimenting different values for "game.tick % 5".
Test mode
Searching Flashlight
[WIP]Fluid handling expansion
[WIP]PvP gamescript
[WIP]Rocket Express
Autofill: The torch has been pass to Nexela
EurypteriD
Inserter
Inserter
Posts: 49
Joined: Wed Jun 04, 2014 11:00 am
Contact:

Re: Need a hand decoding some code i have to maintain.

Post by EurypteriD »

rk84 wrote:There is at least couple things that need to be alter to work with new names.

I suggest you add list of new splitter names to begin of control.lua
e.g.

Code: Select all

local IsSmartSplitter= {
  "basicsmartsplitter" = true,
  "bettersmartsplitter" = true,
  "bestsmartsplitter" = true
}
Hey RK84 ive added this section and all the other changes we have talked about

Code loads and all seems well untill creating a new game i get this error

Code: Select all

__ smartsplitters__/control.lua:966:'}' Expected(toclose '}' at line 5) near '=' 
I have tried wrapping this around several ways even splitting it out into 3 secments.
Let's Play Youtube Channel
Post Reply

Return to “Modding help”