mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 04:01:32 -06:00
Adventure: implement new game (#1531)
Adds Adventure for the Atari 2600, NTSC version. New randomizer, not based on prior works. Somewhat atypical of current AP rom patch games; The generator does not require the adventure rom, but writes some data to an .apadvn APContainer file that the client uses along with a base bsdiff patch to generate a final rom file.
This commit is contained in:
851
data/lua/ADVENTURE/adventure_connector.lua
Normal file
851
data/lua/ADVENTURE/adventure_connector.lua
Normal file
@@ -0,0 +1,851 @@
|
||||
local socket = require("socket")
|
||||
local json = require('json')
|
||||
local math = require('math')
|
||||
|
||||
local STATE_OK = "Ok"
|
||||
local STATE_TENTATIVELY_CONNECTED = "Tentatively Connected"
|
||||
local STATE_INITIAL_CONNECTION_MADE = "Initial Connection Made"
|
||||
local STATE_UNINITIALIZED = "Uninitialized"
|
||||
|
||||
local SCRIPT_VERSION = 1
|
||||
|
||||
local APItemValue = 0xA2
|
||||
local APItemRam = 0xE7
|
||||
local BatAPItemValue = 0xAB
|
||||
local BatAPItemRam = 0xEA
|
||||
local PlayerRoomAddr = 0x8A -- if in number room, we're not in play mode
|
||||
local WinAddr = 0xDE -- if not 0 (I think if 0xff specifically), we won (and should update once, immediately)
|
||||
|
||||
-- If any of these are 2, that dragon ate the player (should send update immediately
|
||||
-- once, and reset that when none of them are 2 again)
|
||||
|
||||
local DragonState = {0xA8, 0xAD, 0xB2}
|
||||
local last_dragon_state = {0, 0, 0}
|
||||
local carryAddress = 0x9D -- uses rom object table
|
||||
local batRoomAddr = 0xCB
|
||||
local batCarryAddress = 0xD0 -- uses ram object location
|
||||
local batInvalidCarryItem = 0x78
|
||||
local batItemCheckAddr = 0xf69f
|
||||
local batMatrixLen = 11 -- number of pairs
|
||||
local last_carry_item = 0xB4
|
||||
local frames_with_no_item = 0
|
||||
local ItemTableStart = 0xfe9d
|
||||
local PlayerSlotAddress = 0xfff9
|
||||
|
||||
local itemMessages = {}
|
||||
|
||||
local nullObjectId = 0xB4
|
||||
local ItemsReceived = nil
|
||||
local sha256hash = nil
|
||||
local foreign_items = nil
|
||||
local foreign_items_by_room = {}
|
||||
local bat_no_touch_locations_by_room = {}
|
||||
local bat_no_touch_items = {}
|
||||
local autocollect_items = {}
|
||||
local localItemLocations = {}
|
||||
|
||||
local prev_bat_room = 0xff
|
||||
local prev_player_room = 0
|
||||
local prev_ap_room_index = nil
|
||||
|
||||
local pending_foreign_items_collected = {}
|
||||
local pending_local_items_collected = {}
|
||||
local rendering_foreign_item = nil
|
||||
local skip_inventory_items = {}
|
||||
|
||||
local inventory = {}
|
||||
local next_inventory_item = nil
|
||||
|
||||
local input_button_address = 0xD7
|
||||
|
||||
local deathlink_rec = nil
|
||||
local deathlink_send = 0
|
||||
|
||||
local deathlink_sent = false
|
||||
|
||||
local prevstate = ""
|
||||
local curstate = STATE_UNINITIALIZED
|
||||
local atariSocket = nil
|
||||
local frame = 0
|
||||
|
||||
local ItemIndex = 0
|
||||
|
||||
local yorgle_speed_address = 0xf725
|
||||
local grundle_speed_address = 0xf740
|
||||
local rhindle_speed_address = 0xf70A
|
||||
|
||||
local read_switch_a = 0xf780
|
||||
local read_switch_b = 0xf764
|
||||
|
||||
local yorgle_speed = nil
|
||||
local grundle_speed = nil
|
||||
local rhindle_speed = nil
|
||||
|
||||
local slow_yorgle_id = tostring(118000000 + 0x103)
|
||||
local slow_grundle_id = tostring(118000000 + 0x104)
|
||||
local slow_rhindle_id = tostring(118000000 + 0x105)
|
||||
|
||||
local yorgle_dead = false
|
||||
local grundle_dead = false
|
||||
local rhindle_dead = false
|
||||
|
||||
local diff_a_locked = false
|
||||
local diff_b_locked = false
|
||||
|
||||
local bat_logic = 0
|
||||
|
||||
local is_dead = 0
|
||||
local freeincarnates_available = 0
|
||||
local send_freeincarnate_used = false
|
||||
local current_bat_ap_item = nil
|
||||
|
||||
local was_in_number_room = false
|
||||
|
||||
local u8 = nil
|
||||
local wU8 = nil
|
||||
local u16
|
||||
|
||||
local bizhawk_version = client.getversion()
|
||||
local is23Or24Or25 = (bizhawk_version=="2.3.1") or (bizhawk_version:sub(1,3)=="2.4") or (bizhawk_version:sub(1,3)=="2.5")
|
||||
local is26To28 = (bizhawk_version:sub(1,3)=="2.6") or (bizhawk_version:sub(1,3)=="2.7") or (bizhawk_version:sub(1,3)=="2.8")
|
||||
|
||||
u8 = memory.read_u8
|
||||
wU8 = memory.write_u8
|
||||
u16 = memory.read_u16_le
|
||||
function uRangeRam(address, bytes)
|
||||
data = memory.read_bytes_as_array(address, bytes, "Main RAM")
|
||||
return data
|
||||
end
|
||||
function uRangeRom(address, bytes)
|
||||
data = memory.read_bytes_as_array(address+0xf000, bytes, "System Bus")
|
||||
return data
|
||||
end
|
||||
function uRangeAddress(address, bytes)
|
||||
data = memory.read_bytes_as_array(address, bytes, "System Bus")
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
function table.empty (self)
|
||||
for _, _ in pairs(self) do
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function slice (tbl, s, e)
|
||||
local pos, new = 1, {}
|
||||
for i = s + 1, e do
|
||||
new[pos] = tbl[i]
|
||||
pos = pos + 1
|
||||
end
|
||||
return new
|
||||
end
|
||||
|
||||
local function createForeignItemsByRoom()
|
||||
foreign_items_by_room = {}
|
||||
if foreign_items == nil then
|
||||
return
|
||||
end
|
||||
for _, foreign_item in pairs(foreign_items) do
|
||||
if foreign_items_by_room[foreign_item.room_id] == nil then
|
||||
foreign_items_by_room[foreign_item.room_id] = {}
|
||||
end
|
||||
new_foreign_item = {}
|
||||
new_foreign_item.room_id = foreign_item.room_id
|
||||
new_foreign_item.room_x = foreign_item.room_x
|
||||
new_foreign_item.room_y = foreign_item.room_y
|
||||
new_foreign_item.short_location_id = foreign_item.short_location_id
|
||||
|
||||
table.insert(foreign_items_by_room[foreign_item.room_id], new_foreign_item)
|
||||
end
|
||||
end
|
||||
|
||||
function debugPrintNoTouchLocations()
|
||||
for room_id, list in pairs(bat_no_touch_locations_by_room) do
|
||||
for index, notouch_location in ipairs(list) do
|
||||
print("ROOM "..tostring(room_id).. "["..tostring(index).."]: "..tostring(notouch_location.short_location_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function processBlock(block)
|
||||
if block == nil then
|
||||
return
|
||||
end
|
||||
local block_identified = 0
|
||||
local msgBlock = block['messages']
|
||||
if msgBlock ~= nil then
|
||||
block_identified = 1
|
||||
for i, v in pairs(msgBlock) do
|
||||
if itemMessages[i] == nil then
|
||||
local msg = {TTL=450, message=v, color=0xFFFF0000}
|
||||
itemMessages[i] = msg
|
||||
end
|
||||
end
|
||||
end
|
||||
local itemsBlock = block["items"]
|
||||
if itemsBlock ~= nil then
|
||||
block_identified = 1
|
||||
ItemsReceived = itemsBlock
|
||||
end
|
||||
local apItemsBlock = block["foreign_items"]
|
||||
if apItemsBlock ~= nil then
|
||||
block_identified = 1
|
||||
print("got foreign items block")
|
||||
foreign_items = apItemsBlock
|
||||
createForeignItemsByRoom()
|
||||
end
|
||||
local autocollectItems = block["autocollect_items"]
|
||||
if autocollectItems ~= nil then
|
||||
block_identified = 1
|
||||
autocollect_items = {}
|
||||
for _, acitem in pairs(autocollectItems) do
|
||||
if autocollect_items[acitem.room_id] == nil then
|
||||
autocollect_items[acitem.room_id] = {}
|
||||
end
|
||||
table.insert(autocollect_items[acitem.room_id], acitem)
|
||||
end
|
||||
end
|
||||
local localLocalItemLocations = block["local_item_locations"]
|
||||
if localLocalItemLocations ~= nil then
|
||||
block_identified = 1
|
||||
localItemLocations = localLocalItemLocations
|
||||
print("got local item locations")
|
||||
end
|
||||
local checkedLocationsBlock = block["checked_locations"]
|
||||
if checkedLocationsBlock ~= nil then
|
||||
block_identified = 1
|
||||
for room_id, foreign_item_list in pairs(foreign_items_by_room) do
|
||||
for i, foreign_item in pairs(foreign_item_list) do
|
||||
short_id = foreign_item.short_location_id
|
||||
for j, checked_id in pairs(checkedLocationsBlock) do
|
||||
if checked_id == short_id then
|
||||
table.remove(foreign_item_list, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if foreign_items ~= nil then
|
||||
for i, foreign_item in pairs(foreign_items) do
|
||||
short_id = foreign_item.short_location_id
|
||||
for j, checked_id in pairs(checkedLocationsBlock) do
|
||||
if checked_id == short_id then
|
||||
foreign_items[i] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local dragon_speeds_block = block["dragon_speeds"]
|
||||
if dragon_speeds_block ~= nil then
|
||||
block_identified = 1
|
||||
yorgle_speed = dragon_speeds_block[slow_yorgle_id]
|
||||
grundle_speed = dragon_speeds_block[slow_grundle_id]
|
||||
rhindle_speed = dragon_speeds_block[slow_rhindle_id]
|
||||
end
|
||||
local diff_a_block = block["difficulty_a_locked"]
|
||||
if diff_a_block ~= nil then
|
||||
block_identified = 1
|
||||
diff_a_locked = diff_a_block
|
||||
end
|
||||
local diff_b_block = block["difficulty_b_locked"]
|
||||
if diff_b_block ~= nil then
|
||||
block_identified = 1
|
||||
diff_b_locked = diff_b_block
|
||||
end
|
||||
local freeincarnates_available_block = block["freeincarnates_available"]
|
||||
if freeincarnates_available_block ~= nil then
|
||||
block_identified = 1
|
||||
if freeincarnates_available ~= freeincarnates_available_block then
|
||||
freeincarnates_available = freeincarnates_available_block
|
||||
local msg = {TTL=450, message="freeincarnates: "..tostring(freeincarnates_available), color=0xFFFF0000}
|
||||
itemMessages[-2] = msg
|
||||
end
|
||||
end
|
||||
local bat_logic_block = block["bat_logic"]
|
||||
if bat_logic_block ~= nil then
|
||||
block_identified = 1
|
||||
bat_logic = bat_logic_block
|
||||
end
|
||||
local bat_no_touch_locations_block = block["bat_no_touch_locations"]
|
||||
if bat_no_touch_locations_block ~= nil then
|
||||
block_identified = 1
|
||||
for _, notouch_location in pairs(bat_no_touch_locations_block) do
|
||||
local room_id = tonumber(notouch_location.room_id)
|
||||
if bat_no_touch_locations_by_room[room_id] == nil then
|
||||
bat_no_touch_locations_by_room[room_id] = {}
|
||||
end
|
||||
table.insert(bat_no_touch_locations_by_room[room_id], notouch_location)
|
||||
|
||||
if notouch_location.local_item ~= nil and notouch_location.local_item ~= 255 then
|
||||
bat_no_touch_items[tonumber(notouch_location.local_item)] = true
|
||||
-- print("no touch: "..tostring(notouch_location.local_item))
|
||||
end
|
||||
end
|
||||
-- debugPrintNoTouchLocations()
|
||||
end
|
||||
deathlink_rec = deathlink_rec or block["deathlink"]
|
||||
if( block_identified == 0 ) then
|
||||
print("unidentified block")
|
||||
print(block)
|
||||
end
|
||||
end
|
||||
|
||||
local function clearScreen()
|
||||
if is23Or24Or25 then
|
||||
return
|
||||
elseif is26To28 then
|
||||
drawText(0, 0, "", "black")
|
||||
end
|
||||
end
|
||||
|
||||
local function getMaxMessageLength()
|
||||
if is23Or24Or25 then
|
||||
return client.screenwidth()/11
|
||||
elseif is26To28 then
|
||||
return client.screenwidth()/12
|
||||
end
|
||||
end
|
||||
|
||||
function drawText(x, y, message, color)
|
||||
if is23Or24Or25 then
|
||||
gui.addmessage(message)
|
||||
elseif is26To28 then
|
||||
gui.drawText(x, y, message, color, 0xB0000000, 18, "Courier New", nil, nil, nil, "client")
|
||||
end
|
||||
end
|
||||
|
||||
local function drawMessages()
|
||||
if table.empty(itemMessages) then
|
||||
clearScreen()
|
||||
return
|
||||
end
|
||||
local y = 10
|
||||
found = false
|
||||
maxMessageLength = getMaxMessageLength()
|
||||
for k, v in pairs(itemMessages) do
|
||||
if v["TTL"] > 0 then
|
||||
message = v["message"]
|
||||
while true do
|
||||
drawText(5, y, message:sub(1, maxMessageLength), v["color"])
|
||||
y = y + 16
|
||||
|
||||
message = message:sub(maxMessageLength + 1, message:len())
|
||||
if message:len() == 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
newTTL = 0
|
||||
if is26To28 then
|
||||
newTTL = itemMessages[k]["TTL"] - 1
|
||||
end
|
||||
itemMessages[k]["TTL"] = newTTL
|
||||
found = true
|
||||
end
|
||||
end
|
||||
if found == false then
|
||||
clearScreen()
|
||||
end
|
||||
end
|
||||
|
||||
function difference(a, b)
|
||||
local aa = {}
|
||||
for k,v in pairs(a) do aa[v]=true end
|
||||
for k,v in pairs(b) do aa[v]=nil end
|
||||
local ret = {}
|
||||
local n = 0
|
||||
for k,v in pairs(a) do
|
||||
if aa[v] then n=n+1 ret[n]=v end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function getAllRam()
|
||||
uRangeRAM(0,128);
|
||||
return data
|
||||
end
|
||||
|
||||
local function arrayEqual(a1, a2)
|
||||
if #a1 ~= #a2 then
|
||||
return false
|
||||
end
|
||||
|
||||
for i, v in ipairs(a1) do
|
||||
if v ~= a2[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function alive_mode()
|
||||
return (u8(PlayerRoomAddr) ~= 0x00 and u8(WinAddr) == 0x00)
|
||||
end
|
||||
|
||||
local function generateLocationsChecked()
|
||||
list_of_locations = {}
|
||||
for s, f in pairs(pending_foreign_items_collected) do
|
||||
table.insert(list_of_locations, f.short_location_id + 118000000)
|
||||
end
|
||||
for s, f in pairs(pending_local_items_collected) do
|
||||
table.insert(list_of_locations, f + 118000000)
|
||||
end
|
||||
return list_of_locations
|
||||
end
|
||||
|
||||
function receive()
|
||||
l, e = atariSocket:receive()
|
||||
if e == 'closed' then
|
||||
if curstate == STATE_OK then
|
||||
print("Connection closed")
|
||||
end
|
||||
curstate = STATE_UNINITIALIZED
|
||||
return
|
||||
elseif e == 'timeout' then
|
||||
return
|
||||
elseif e ~= nil then
|
||||
print(e)
|
||||
curstate = STATE_UNINITIALIZED
|
||||
return
|
||||
end
|
||||
if l ~= nil then
|
||||
processBlock(json.decode(l))
|
||||
end
|
||||
-- Determine Message to send back
|
||||
|
||||
newSha256 = memory.hash_region(0xF000, 0x1000, "System Bus")
|
||||
if (sha256hash ~= nil and sha256hash ~= newSha256) then
|
||||
print("ROM changed, quitting")
|
||||
curstate = STATE_UNINITIALIZED
|
||||
return
|
||||
end
|
||||
sha256hash = newSha256
|
||||
local retTable = {}
|
||||
retTable["scriptVersion"] = SCRIPT_VERSION
|
||||
retTable["romhash"] = sha256hash
|
||||
if (alive_mode()) then
|
||||
retTable["locations"] = generateLocationsChecked()
|
||||
end
|
||||
if (u8(WinAddr) ~= 0x00) then
|
||||
retTable["victory"] = 1
|
||||
end
|
||||
if( deathlink_sent or deathlink_send == 0 ) then
|
||||
retTable["deathLink"] = 0
|
||||
else
|
||||
print("Sending deathlink "..tostring(deathlink_send))
|
||||
retTable["deathLink"] = deathlink_send
|
||||
deathlink_sent = true
|
||||
end
|
||||
deathlink_send = 0
|
||||
|
||||
if send_freeincarnate_used == true then
|
||||
print("Sending freeincarnate used")
|
||||
retTable["freeincarnate"] = true
|
||||
send_freeincarnate_used = false
|
||||
end
|
||||
|
||||
msg = json.encode(retTable).."\n"
|
||||
local ret, error = atariSocket:send(msg)
|
||||
if ret == nil then
|
||||
print(error)
|
||||
elseif curstate == STATE_INITIAL_CONNECTION_MADE then
|
||||
curstate = STATE_TENTATIVELY_CONNECTED
|
||||
elseif curstate == STATE_TENTATIVELY_CONNECTED then
|
||||
print("Connected!")
|
||||
curstate = STATE_OK
|
||||
end
|
||||
end
|
||||
|
||||
function AutocollectFromRoom()
|
||||
if autocollect_items ~= nil and autocollect_items[prev_player_room] ~= nil then
|
||||
for _, item in pairs(autocollect_items[prev_player_room]) do
|
||||
pending_foreign_items_collected[item.short_location_id] = item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SetYorgleSpeed()
|
||||
if yorgle_speed ~= nil then
|
||||
emu.setregister("A", yorgle_speed);
|
||||
end
|
||||
end
|
||||
|
||||
function SetGrundleSpeed()
|
||||
if grundle_speed ~= nil then
|
||||
emu.setregister("A", grundle_speed);
|
||||
end
|
||||
end
|
||||
|
||||
function SetRhindleSpeed()
|
||||
if rhindle_speed ~= nil then
|
||||
emu.setregister("A", rhindle_speed);
|
||||
end
|
||||
end
|
||||
|
||||
function SetDifficultySwitchB()
|
||||
if diff_b_locked then
|
||||
local a = emu.getregister("A")
|
||||
if a < 128 then
|
||||
emu.setregister("A", a + 128)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SetDifficultySwitchA()
|
||||
if diff_a_locked then
|
||||
local a = emu.getregister("A")
|
||||
if (a > 128 and a < 128 + 64) or (a < 64) then
|
||||
emu.setregister("A", a + 64)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function TryFreeincarnate()
|
||||
if freeincarnates_available > 0 then
|
||||
freeincarnates_available = freeincarnates_available - 1
|
||||
for index, state_addr in pairs(DragonState) do
|
||||
if last_dragon_state[index] == 1 then
|
||||
send_freeincarnate_used = true
|
||||
memory.write_u8(state_addr, 1, "System Bus")
|
||||
local msg = {TTL=450, message="used freeincarnate", color=0xFF00FF00}
|
||||
itemMessages[-1] = msg
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function GetLinkedObject()
|
||||
if emu.getregister("X") == batRoomAddr then
|
||||
bat_interest_item = emu.getregister("A")
|
||||
-- if the bat can't touch that item, we'll switch it to the number item, which should never be
|
||||
-- in the same room as the bat.
|
||||
if bat_no_touch_items[bat_interest_item] ~= nil then
|
||||
emu.setregister("A", 0xDD )
|
||||
emu.setregister("Y", 0xDD )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CheckCollectAPItem(carry_item, target_item_value, target_item_ram, rendering_foreign_item)
|
||||
if( carry_item == target_item_value and rendering_foreign_item ~= nil ) then
|
||||
memory.write_u8(carryAddress, nullObjectId, "System Bus")
|
||||
memory.write_u8(target_item_ram, 0xFF, "System Bus")
|
||||
pending_foreign_items_collected[rendering_foreign_item.short_location_id] = rendering_foreign_item
|
||||
for index, fi in pairs(foreign_items_by_room[rendering_foreign_item.room_id]) do
|
||||
if( fi.short_location_id == rendering_foreign_item.short_location_id ) then
|
||||
table.remove(foreign_items_by_room[rendering_foreign_item.room_id], index)
|
||||
break
|
||||
end
|
||||
end
|
||||
for index, fi in pairs(foreign_items) do
|
||||
if( fi.short_location_id == rendering_foreign_item.short_location_id ) then
|
||||
foreign_items[index] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
prev_ap_room_index = 0
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function BatCanTouchForeign(foreign_item, bat_room)
|
||||
if bat_no_touch_locations_by_room[bat_room] == nil or bat_no_touch_locations_by_room[bat_room][1] == nil then
|
||||
return true
|
||||
end
|
||||
|
||||
for index, location in ipairs(bat_no_touch_locations_by_room[bat_room]) do
|
||||
if location.short_location_id == foreign_item.short_location_id then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true;
|
||||
end
|
||||
|
||||
function main()
|
||||
memory.usememorydomain("System Bus")
|
||||
if (is23Or24Or25 or is26To28) == false then
|
||||
print("Must use a version of bizhawk 2.3.1 or higher")
|
||||
return
|
||||
end
|
||||
local playerSlot = memory.read_u8(PlayerSlotAddress)
|
||||
local port = 17242 + playerSlot
|
||||
print("Using port"..tostring(port))
|
||||
server, error = socket.bind('localhost', port)
|
||||
if( error ~= nil ) then
|
||||
print(error)
|
||||
end
|
||||
event.onmemoryexecute(SetYorgleSpeed, yorgle_speed_address);
|
||||
event.onmemoryexecute(SetGrundleSpeed, grundle_speed_address);
|
||||
event.onmemoryexecute(SetRhindleSpeed, rhindle_speed_address);
|
||||
event.onmemoryexecute(SetDifficultySwitchA, read_switch_a)
|
||||
event.onmemoryexecute(SetDifficultySwitchB, read_switch_b)
|
||||
event.onmemoryexecute(GetLinkedObject, batItemCheckAddr)
|
||||
-- TODO: Add an onmemoryexecute event to intercept the bat reading item rooms, and don't 'see' an item in the
|
||||
-- room if it is in bat_no_touch_locations_by_room. Although realistically, I may have to handle this in the rom
|
||||
-- for it to be totally reliable, because it won't work before the script connects (I might have to reset them?)
|
||||
-- TODO: Also remove those items from the bat_no_touch_locations_by_room if they have been collected
|
||||
while true do
|
||||
frame = frame + 1
|
||||
drawMessages()
|
||||
if not (curstate == prevstate) then
|
||||
print("Current state: "..curstate)
|
||||
prevstate = curstate
|
||||
end
|
||||
|
||||
local current_player_room = u8(PlayerRoomAddr)
|
||||
local bat_room = u8(batRoomAddr)
|
||||
local bat_carrying_item = u8(batCarryAddress)
|
||||
local bat_carrying_ap_item = (BatAPItemRam == bat_carrying_item)
|
||||
|
||||
if current_player_room == 0x1E then
|
||||
if u8(PlayerRoomAddr + 1) > 0x4B then
|
||||
memory.write_u8(PlayerRoomAddr + 1, 0x4B)
|
||||
end
|
||||
end
|
||||
|
||||
if current_player_room == 0x00 then
|
||||
if not was_in_number_room then
|
||||
print("reset "..tostring(bat_carrying_ap_item).." "..tostring(bat_carrying_item))
|
||||
memory.write_u8(batCarryAddress, batInvalidCarryItem)
|
||||
memory.write_u8(batCarryAddress+ 1, 0)
|
||||
createForeignItemsByRoom()
|
||||
memory.write_u8(BatAPItemRam, 0xff)
|
||||
memory.write_u8(APItemRam, 0xff)
|
||||
prev_ap_room_index = 0
|
||||
prev_player_room = 0
|
||||
rendering_foreign_item = nil
|
||||
was_in_number_room = true
|
||||
end
|
||||
else
|
||||
was_in_number_room = false
|
||||
end
|
||||
|
||||
if bat_room ~= prev_bat_room then
|
||||
if bat_carrying_ap_item then
|
||||
if foreign_items_by_room[prev_bat_room] ~= nil then
|
||||
for r,f in pairs(foreign_items_by_room[prev_bat_room]) do
|
||||
if f.short_location_id == current_bat_ap_item.short_location_id then
|
||||
-- print("removing item from "..tostring(r).." in "..tostring(prev_bat_room))
|
||||
table.remove(foreign_items_by_room[prev_bat_room], r)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if foreign_items_by_room[bat_room] == nil then
|
||||
foreign_items_by_room[bat_room] = {}
|
||||
end
|
||||
-- print("adding item to "..tostring(bat_room))
|
||||
table.insert(foreign_items_by_room[bat_room], current_bat_ap_item)
|
||||
else
|
||||
-- set AP item room and position for new room, or to invalid room
|
||||
if foreign_items_by_room[bat_room] ~= nil and foreign_items_by_room[bat_room][1] ~= nil
|
||||
and BatCanTouchForeign(foreign_items_by_room[bat_room][1], bat_room) then
|
||||
if current_bat_ap_item ~= foreign_items_by_room[bat_room][1] then
|
||||
current_bat_ap_item = foreign_items_by_room[bat_room][1]
|
||||
-- print("Changing bat item to "..tostring(current_bat_ap_item.short_location_id))
|
||||
end
|
||||
memory.write_u8(BatAPItemRam, bat_room)
|
||||
memory.write_u8(BatAPItemRam + 1, current_bat_ap_item.room_x)
|
||||
memory.write_u8(BatAPItemRam + 2, current_bat_ap_item.room_y)
|
||||
else
|
||||
memory.write_u8(BatAPItemRam, 0xff)
|
||||
if current_bat_ap_item ~= nil then
|
||||
-- print("clearing bat item")
|
||||
end
|
||||
current_bat_ap_item = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
prev_bat_room = bat_room
|
||||
|
||||
-- update foreign_items_by_room position and room id for bat item if bat carrying an item
|
||||
if bat_carrying_ap_item then
|
||||
-- this is setting the item using the bat's position, which is somewhat wrong, but I think
|
||||
-- there will be more problems with the room not matching sometimes if I use the actual item position
|
||||
current_bat_ap_item.room_id = bat_room
|
||||
current_bat_ap_item.room_x = u8(batRoomAddr + 1)
|
||||
current_bat_ap_item.room_y = u8(batRoomAddr + 2)
|
||||
end
|
||||
|
||||
if (alive_mode()) then
|
||||
if (current_player_room ~= prev_player_room) then
|
||||
memory.write_u8(APItemRam, 0xFF, "System Bus")
|
||||
prev_ap_room_index = 0
|
||||
prev_player_room = current_player_room
|
||||
AutocollectFromRoom()
|
||||
end
|
||||
local carry_item = memory.read_u8(carryAddress, "System Bus")
|
||||
bat_no_touch_items[carry_item] = nil
|
||||
if (next_inventory_item ~= nil) then
|
||||
if ( carry_item == nullObjectId and last_carry_item == nullObjectId ) then
|
||||
frames_with_no_item = frames_with_no_item + 1
|
||||
if (frames_with_no_item > 10) then
|
||||
frames_with_no_item = 10
|
||||
local input_value = memory.read_u8(input_button_address, "System Bus")
|
||||
if( input_value >= 64 and input_value < 128 ) then -- high bit clear, second highest bit set
|
||||
memory.write_u8(carryAddress, next_inventory_item)
|
||||
local item_ram_location = memory.read_u8(ItemTableStart + next_inventory_item)
|
||||
if( memory.read_u8(batCarryAddress) ~= 0x78 and
|
||||
memory.read_u8(batCarryAddress) == item_ram_location) then
|
||||
memory.write_u8(batCarryAddress, batInvalidCarryItem)
|
||||
memory.write_u8(batCarryAddress+ 1, 0)
|
||||
memory.write_u8(item_ram_location, current_player_room)
|
||||
memory.write_u8(item_ram_location + 1, memory.read_u8(PlayerRoomAddr + 1))
|
||||
memory.write_u8(item_ram_location + 2, memory.read_u8(PlayerRoomAddr + 2))
|
||||
end
|
||||
ItemIndex = ItemIndex + 1
|
||||
next_inventory_item = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
frames_with_no_item = 0
|
||||
end
|
||||
end
|
||||
if( carry_item ~= last_carry_item ) then
|
||||
if ( localItemLocations ~= nil and localItemLocations[tostring(carry_item)] ~= nil ) then
|
||||
pending_local_items_collected[localItemLocations[tostring(carry_item)]] =
|
||||
localItemLocations[tostring(carry_item)]
|
||||
table.remove(localItemLocations, tostring(carry_item))
|
||||
skip_inventory_items[carry_item] = carry_item
|
||||
end
|
||||
end
|
||||
last_carry_item = carry_item
|
||||
|
||||
CheckCollectAPItem(carry_item, APItemValue, APItemRam, rendering_foreign_item)
|
||||
if CheckCollectAPItem(carry_item, BatAPItemValue, BatAPItemRam, current_bat_ap_item) and bat_carrying_ap_item then
|
||||
memory.write_u8(batCarryAddress, batInvalidCarryItem)
|
||||
memory.write_u8(batCarryAddress+ 1, 0)
|
||||
end
|
||||
|
||||
|
||||
rendering_foreign_item = nil
|
||||
if( foreign_items_by_room[current_player_room] ~= nil ) then
|
||||
if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil ) and memory.read_u8(APItemRam) ~= 0xff then
|
||||
foreign_items_by_room[current_player_room][prev_ap_room_index].room_x = memory.read_u8(APItemRam + 1)
|
||||
foreign_items_by_room[current_player_room][prev_ap_room_index].room_y = memory.read_u8(APItemRam + 2)
|
||||
end
|
||||
prev_ap_room_index = prev_ap_room_index + 1
|
||||
local invalid_index = -1
|
||||
if( foreign_items_by_room[current_player_room][prev_ap_room_index] == nil ) then
|
||||
prev_ap_room_index = 1
|
||||
end
|
||||
if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil and current_bat_ap_item ~= nil and
|
||||
foreign_items_by_room[current_player_room][prev_ap_room_index].short_location_id == current_bat_ap_item.short_location_id) then
|
||||
invalid_index = prev_ap_room_index
|
||||
prev_ap_room_index = prev_ap_room_index + 1
|
||||
if( foreign_items_by_room[current_player_room][prev_ap_room_index] == nil ) then
|
||||
prev_ap_room_index = 1
|
||||
end
|
||||
end
|
||||
|
||||
if( foreign_items_by_room[current_player_room][prev_ap_room_index] ~= nil and prev_ap_room_index ~= invalid_index ) then
|
||||
memory.write_u8(APItemRam, current_player_room)
|
||||
rendering_foreign_item = foreign_items_by_room[current_player_room][prev_ap_room_index]
|
||||
memory.write_u8(APItemRam + 1, rendering_foreign_item.room_x)
|
||||
memory.write_u8(APItemRam + 2, rendering_foreign_item.room_y)
|
||||
else
|
||||
memory.write_u8(APItemRam, 0xFF, "System Bus")
|
||||
end
|
||||
end
|
||||
if is_dead == 0 then
|
||||
dragons_revived = false
|
||||
player_dead = false
|
||||
new_dragon_state = {0,0,0}
|
||||
for index, dragon_state_addr in pairs(DragonState) do
|
||||
new_dragon_state[index] = memory.read_u8(dragon_state_addr, "System Bus" )
|
||||
if last_dragon_state[index] == 1 and new_dragon_state[index] ~= 1 then
|
||||
dragons_revived = true
|
||||
elseif last_dragon_state[index] ~= 1 and new_dragon_state[index] == 1 then
|
||||
dragon_real_index = index - 1
|
||||
print("Killed dragon: "..tostring(dragon_real_index))
|
||||
local dragon_item = {}
|
||||
dragon_item["short_location_id"] = 0xD0 + dragon_real_index
|
||||
pending_foreign_items_collected[dragon_item.short_location_id] = dragon_item
|
||||
end
|
||||
if new_dragon_state[index] == 2 then
|
||||
player_dead = true
|
||||
end
|
||||
end
|
||||
if dragons_revived and player_dead == false then
|
||||
TryFreeincarnate()
|
||||
end
|
||||
last_dragon_state = new_dragon_state
|
||||
end
|
||||
elseif (u8(PlayerRoomAddr) == 0x00) then -- not alive mode, in number room
|
||||
ItemIndex = 0 -- reset our inventory
|
||||
next_inventory_item = nil
|
||||
skip_inventory_items = {}
|
||||
end
|
||||
if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
|
||||
if (frame % 5 == 0) then
|
||||
receive()
|
||||
if alive_mode() then
|
||||
local was_dead = is_dead
|
||||
is_dead = 0
|
||||
for index, dragonStateAddr in pairs(DragonState) do
|
||||
local dragonstateval = memory.read_u8(dragonStateAddr, "System Bus")
|
||||
if ( dragonstateval == 2) then
|
||||
is_dead = index
|
||||
end
|
||||
end
|
||||
if was_dead ~= 0 and is_dead == 0 then
|
||||
TryFreeincarnate()
|
||||
end
|
||||
if deathlink_rec == true and is_dead == 0 then
|
||||
print("setting dead from deathlink")
|
||||
deathlink_rec = false
|
||||
deathlink_sent = true
|
||||
is_dead = 1
|
||||
memory.write_u8(carryAddress, nullObjectId, "System Bus")
|
||||
memory.write_u8(DragonState[1], 2, "System Bus")
|
||||
end
|
||||
if (is_dead > 0 and deathlink_send == 0 and not deathlink_sent) then
|
||||
deathlink_send = is_dead
|
||||
print("setting deathlink_send to "..tostring(is_dead))
|
||||
elseif (is_dead == 0) then
|
||||
deathlink_send = 0
|
||||
deathlink_sent = false
|
||||
end
|
||||
if ItemsReceived ~= nil and ItemsReceived[ItemIndex + 1] ~= nil then
|
||||
while ItemsReceived[ItemIndex + 1] ~= nil and skip_inventory_items[ItemsReceived[ItemIndex + 1]] ~= nil do
|
||||
print("skip")
|
||||
ItemIndex = ItemIndex + 1
|
||||
end
|
||||
local static_id = ItemsReceived[ItemIndex + 1]
|
||||
if static_id ~= nil then
|
||||
inventory[static_id] = 1
|
||||
if next_inventory_item == nil then
|
||||
next_inventory_item = static_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif (curstate == STATE_UNINITIALIZED) then
|
||||
if (frame % 60 == 0) then
|
||||
|
||||
print("Waiting for client.")
|
||||
|
||||
emu.frameadvance()
|
||||
server:settimeout(2)
|
||||
print("Attempting to connect")
|
||||
local client, timeout = server:accept()
|
||||
if timeout == nil then
|
||||
print("Initial connection made")
|
||||
curstate = STATE_INITIAL_CONNECTION_MADE
|
||||
atariSocket = client
|
||||
atariSocket:settimeout(0)
|
||||
end
|
||||
end
|
||||
end
|
||||
emu.frameadvance()
|
||||
end
|
||||
end
|
||||
|
||||
main()
|
380
data/lua/ADVENTURE/json.lua
Normal file
380
data/lua/ADVENTURE/json.lua
Normal file
@@ -0,0 +1,380 @@
|
||||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2015 rxi
|
||||
--
|
||||
-- This library is free software; you can redistribute it and/or modify it
|
||||
-- under the terms of the MIT license. See LICENSE for details.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.0" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\\\",
|
||||
[ "\"" ] = "\\\"",
|
||||
[ "\b" ] = "\\b",
|
||||
[ "\f" ] = "\\f",
|
||||
[ "\n" ] = "\\n",
|
||||
[ "\r" ] = "\\r",
|
||||
[ "\t" ] = "\\t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "\\/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return escape_char_map[c] or string.format("\\u%04x", c:byte())
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if val[1] ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
--local line_count = 1
|
||||
--local col_count = 1
|
||||
--for i = 1, idx - 1 do
|
||||
-- col_count = col_count + 1
|
||||
-- if str:sub(i, i) == "\n" then
|
||||
-- line_count = line_count + 1
|
||||
-- col_count = 1
|
||||
-- end
|
||||
-- end
|
||||
-- emu.message( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(3, 6), 16 )
|
||||
local n2 = tonumber( s:sub(9, 12), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local has_unicode_escape = false
|
||||
local has_surrogate_escape = false
|
||||
local has_escape = false
|
||||
local last
|
||||
for j = i + 1, #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
end
|
||||
|
||||
if last == 92 then -- "\\" (escape char)
|
||||
if x == 117 then -- "u" (unicode escape sequence)
|
||||
local hex = str:sub(j + 1, j + 5)
|
||||
if not hex:find("%x%x%x%x") then
|
||||
decode_error(str, j, "invalid unicode escape in string")
|
||||
end
|
||||
if hex:find("^[dD][89aAbB]") then
|
||||
has_surrogate_escape = true
|
||||
else
|
||||
has_unicode_escape = true
|
||||
end
|
||||
else
|
||||
local c = string.char(x)
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
has_escape = true
|
||||
end
|
||||
last = nil
|
||||
|
||||
elseif x == 34 then -- '"' (end of string)
|
||||
local s = str:sub(i + 1, j - 1)
|
||||
if has_surrogate_escape then
|
||||
s = s:gsub("\\u[dD][89aAbB]..\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_unicode_escape then
|
||||
s = s:gsub("\\u....", parse_unicode_escape)
|
||||
end
|
||||
if has_escape then
|
||||
s = s:gsub("\\.", escape_char_map_inv)
|
||||
end
|
||||
return s, j + 1
|
||||
|
||||
else
|
||||
last = x
|
||||
end
|
||||
end
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
return ( parse(str, next_char(str, 1, space_chars, true)) )
|
||||
end
|
||||
|
||||
|
||||
return json
|
132
data/lua/ADVENTURE/socket.lua
Normal file
132
data/lua/ADVENTURE/socket.lua
Normal file
@@ -0,0 +1,132 @@
|
||||
-----------------------------------------------------------------------------
|
||||
-- LuaSocket helper module
|
||||
-- Author: Diego Nehab
|
||||
-- RCS ID: $Id: socket.lua,v 1.22 2005/11/22 08:33:29 diego Exp $
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Declare module and import dependencies
|
||||
-----------------------------------------------------------------------------
|
||||
local base = _G
|
||||
local string = require("string")
|
||||
local math = require("math")
|
||||
local socket = require("socket.core")
|
||||
module("socket")
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Exported auxiliar functions
|
||||
-----------------------------------------------------------------------------
|
||||
function connect(address, port, laddress, lport)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
if laddress then
|
||||
local res, err = sock:bind(laddress, lport, -1)
|
||||
if not res then return nil, err end
|
||||
end
|
||||
local res, err = sock:connect(address, port)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
function bind(host, port, backlog)
|
||||
local sock, err = socket.tcp()
|
||||
if not sock then return nil, err end
|
||||
sock:setoption("reuseaddr", true)
|
||||
local res, err = sock:bind(host, port)
|
||||
if not res then return nil, err end
|
||||
res, err = sock:listen(backlog)
|
||||
if not res then return nil, err end
|
||||
return sock
|
||||
end
|
||||
|
||||
try = newtry()
|
||||
|
||||
function choose(table)
|
||||
return function(name, opt1, opt2)
|
||||
if base.type(name) ~= "string" then
|
||||
name, opt1, opt2 = "default", name, opt1
|
||||
end
|
||||
local f = table[name or "nil"]
|
||||
if not f then base.error("unknown key (".. base.tostring(name) ..")", 3)
|
||||
else return f(opt1, opt2) end
|
||||
end
|
||||
end
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
-- Socket sources and sinks, conforming to LTN12
|
||||
-----------------------------------------------------------------------------
|
||||
-- create namespaces inside LuaSocket namespace
|
||||
sourcet = {}
|
||||
sinkt = {}
|
||||
|
||||
BLOCKSIZE = 2048
|
||||
|
||||
sinkt["close-when-done"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if not chunk then
|
||||
sock:close()
|
||||
return 1
|
||||
else return sock:send(chunk) end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["keep-open"] = function(sock)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function(self, chunk, err)
|
||||
if chunk then return sock:send(chunk)
|
||||
else return 1 end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sinkt["default"] = sinkt["keep-open"]
|
||||
|
||||
sink = choose(sinkt)
|
||||
|
||||
sourcet["by-length"] = function(sock, length)
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if length <= 0 then return nil end
|
||||
local size = math.min(socket.BLOCKSIZE, length)
|
||||
local chunk, err = sock:receive(size)
|
||||
if err then return nil, err end
|
||||
length = length - string.len(chunk)
|
||||
return chunk
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
sourcet["until-closed"] = function(sock)
|
||||
local done
|
||||
return base.setmetatable({
|
||||
getfd = function() return sock:getfd() end,
|
||||
dirty = function() return sock:dirty() end
|
||||
}, {
|
||||
__call = function()
|
||||
if done then return nil end
|
||||
local chunk, err, partial = sock:receive(socket.BLOCKSIZE)
|
||||
if not err then return chunk
|
||||
elseif err == "closed" then
|
||||
sock:close()
|
||||
done = 1
|
||||
return partial
|
||||
else return nil, err end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
sourcet["default"] = sourcet["until-closed"]
|
||||
|
||||
source = choose(sourcet)
|
Reference in New Issue
Block a user