Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Wait on Async memory record thread.

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
TheyCallMeTim13
Wiki Contributor
Reputation: 50

Joined: 24 Feb 2017
Posts: 976
Location: Pluto

PostPosted: Sat Mar 24, 2018 8:51 am    Post subject: Wait on Async memory record thread. Reply with quote

Is there a way to wait on an async memory record's thread?
_________________
Back to top
View user's profile Send private message Visit poster's website
MateeJr GT
Advanced Cheater
Reputation: 0

Joined: 24 Dec 2017
Posts: 66

PostPosted: Sat Mar 24, 2018 9:00 am    Post subject: Reply with quote

Yes
_________________
Hi Lynxz Gaming
Back to top
View user's profile Send private message
panraven
Grandmaster Cheater
Reputation: 54

Joined: 01 Oct 2008
Posts: 941

PostPosted: Sat Mar 24, 2018 9:13 am    Post subject: Reply with quote

try check AsyncProcessing,
note that Async can be false, ie changed during async execution by user or by script.

_________________
- Retarded.
Back to top
View user's profile Send private message
TheyCallMeTim13
Wiki Contributor
Reputation: 50

Joined: 24 Feb 2017
Posts: 976
Location: Pluto

PostPosted: Sat Mar 24, 2018 10:00 am    Post subject: Reply with quote

panraven wrote:
try check AsyncProcessing,
note that Async can be false, ie changed during async execution by user or by script.


So it tried:
Code:
while mr.Async and mr.AsyncProcessing do
    sleep(1)
end


But it didn't work (EDIT: seems to wait endlessly). And if the memory record is Async then it breaks the order. (EDIT: at lest with nested records.)

Lines 176 and 210:
Full code (records the table's memory records states' and sets them latter):
Code:

local NAME = 'I2 Cheat Engine Table State'
local CLASS_NAME = 'I2CETState'
local VERSION = '1.0.2'
local AUTHOR = 'Matt Irwin; [email protected]'
local LICENSE = [=[MIT License

Copyright (c) 2017 Matt Irwin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]=]

local format = string.format
local strE = string.empty or STRING_EMPTY or ''
local t = translate



I2CETState = {
   Name = NAME,
   ClassName = CLASS_NAME,
   Version = VERSION,
   Author = AUTHOR,
   License = LICENSE,
   DefaultState = 'default',
   SaveFileName = 'I2CETState.${StateName}.txt',
   UseMemoryRecordDescriptions = false,
   LineEnd = '\n',
   DisableBeforeLoad = true,
   PrintStatus = true,
}



-- local Logger = {
--    LEVELS = {
--       OFF = 0,
--       FATAL = 1,
--       ERROR = 2,
--       WARN = 3,
--       INFO = 4,
--       DEBUG = 5,
--       TRACE = 6
--    },
-- }
-- for k, v in pairs(Logger.LEVELS) do
--    -- Logger[k:lower()] = function( ... ) return end
--    -- Logger[k:lower() .. 'f'] = function( ... ) return end
--    Logger[k:lower()] = function(msg, ex) return print(msg, ex) end
--    Logger[k:lower() .. 'f'] = function(msg, ... ) return print(string.format(msg, ... )) end
-- end
local Logger = Logger
if Logger == nil then
   if type(CETrequire) == 'function' then
      Logger = CETrequire('I2CETLogger')
   else
      Logger = require('I2CETLogger')
   end
   Logger.LogName = 'CETstate'
end


if type(CETrequire) == 'function' then
   I2CEHelpers = CETrequire('I2CEHelpers')
else
   I2CEHelpers = require('I2CEHelpers')
end

local function split(s, delimiter)
   return I2CEHelpers.split(s, delimiter)
end

local function interp(s, tbl)
   return I2CEHelpers.interp(s, tbl)
end



function I2CETState.saveTableState(stateName)
   Logger.trace()
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.DefaultState
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   local fileStr = strE
   for i = 0, AddressList.Count - 1 do
      local mr = AddressList.getMemoryRecord(i)
      if mr.Description ~= '_[  I2CETState  ]_' and mr.Description:sub(0, 16) ~= 'Save Table State' then
         if mr.Active then
            local id = tostring(mr.ID)
            if I2CETState.UseMemoryRecordDescriptions then
               id = mr.Description
            end
            fileStr = fileStr .. id .. le
         end
      end
   end
   local f, err = io.open(fileName, 'w')
   if err then
      Logger.errorf('The file could not be opened, "%s", %s', fileName, err)
   elseif f and not err then
      f:write(fileStr)
      f:close()
   end
end

function I2CETState.loadTableState(stateName)
   Logger.trace()
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      getLuaEngine().show()
   end
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.DefaultState
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Setting Table State:  %s', stateName))
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   Logger.debugf('Using state file name: "%s"', fileName)
   local fileStr = nil
   local f, err = io.open(fileName, 'r')
   if err then
      Logger.infof('The local file could not be opened, "%s", %s', fileName, err)
      local tableFile = findTableFile(fileName)
      if tableFile == nil then
         Logger.warnf('file not found, "%s"', fileName)
         return
      end
      local stream = tableFile.getData()
      local bytes = stream.read(stream.Size)
      for i = 1, #bytes do
         if fileStr == nil then
            fileStr = strE
         end
         fileStr = fileStr .. string.char(bytes[i])
      end
   elseif f and not err then
      fileStr = f:read('*all')
      f:close()
   else
      Logger.errorf('The file could not be opened, "%s"', fileName)
   end
   if I2CETState.DisableBeforeLoad then
      for i = AddressList.Count - 1, 0, -1 do
         local mr = AddressList.getMemoryRecord(i)
         if mr.Description ~= '_[  I2CETState  ]_' and mr.Description:sub(0, 16) ~= 'Save Table State' then
            if mr.Active then
               Logger.infof('Disabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  local des = mr.Description--:gsub('_[  ', strE):gsub('  ]_', strE):gsub('-[  ', strE):gsub('  ]-', strE):gsub('  ()->', strE)
                  print(format('Disabling:  %s', des))
               end
               if mr.Async then
                  mr.Async = false
                  sleep(0)
               end
               mr.Active = false
               -- while mr.Async and mr.AsyncProcessing do
               --    sleep(1)
               -- end
               sleep(0)
            end
         end
      end
      sleep(0)
   end
   if fileStr == nil then
      Logger.info('File string empty')
      return
   end
   local lines = split(fileStr, I2CETState.LineEnd)
   for i, v in ipairs(lines) do
      if v ~= nil and v ~= strE then
         local mr = nil
         if I2CETState.UseMemoryRecordDescriptions then
            mr = AddressList.getMemoryRecordByDescription(v)
         else
            mr = AddressList.getMemoryRecordByID(tonumber(v))
         end
         if mr ~= nil then
            if not mr.Active then
               Logger.infof('Enabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  local des = mr.Description--:gsub('_[  ', strE):gsub('  ]_', strE):gsub('-[  ', strE):gsub('  ]-', strE):gsub('  ()->', strE)
                  print(format('Enabling:  %s', des))
               end
               if mr.Async then
                  mr.Async = false
                  sleep(0)
               end
               mr.Active = true
               -- while mr.Async and mr.AsyncProcessing do
               --    sleep(1)
               -- end
               sleep(0)
            end
         end
      end
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Table State Set:  %s', stateName))
      getLuaEngine().hide()
   end
end


return I2CETState

_________________
Back to top
View user's profile Send private message Visit poster's website
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Sat Mar 24, 2018 10:55 am    Post subject: This post has 1 review(s) Reply with quote

If you want to block the main thread, don't execute the script asynchronously. It pretty much defeats the purpose of executing it on a different thread, and there isn't a simple way to check when it's done. The active property will change only if it executes successfully, and the AsyncProcessing property must be changed by the main thread.

The following does not apply to the code you posted above, but if someone is in a rare situation in which blocking the main thread is the best course of action, call checkSynchronize in the wait loop and break when AsyncProcessing is false. This works for CE 6.7, and while I would guess it will work for future versions, I don't believe there's any guarantee.

Memory records also have an OnActivate property that some may find more useful.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
TheyCallMeTim13
Wiki Contributor
Reputation: 50

Joined: 24 Feb 2017
Posts: 976
Location: Pluto

PostPosted: Sat Mar 24, 2018 11:20 am    Post subject: Reply with quote

ParkourPenguin wrote:
If you want to block the main thread, don't execute the script asynchronously. It pretty much defeats the purpose of executing it on a different thread, and there isn't a simple way to check when it's done. The active property will change only if it executes successfully, and the AsyncProcessing property must be changed by the main thread.

The following does not apply to the code you posted above, but if someone is in a rare situation in which blocking the main thread is the best course of action, call checkSynchronize in the wait loop and break when AsyncProcessing is false. This works for CE 6.7, and while I would guess it will work for future versions, I don't believe there's any guarantee.

Memory records also have an OnActivate property that some may find more useful.


So I started to mess with threads, found "checkSynchronize" but didn't think to use it in the while loop, but that worked. Thank you.
Code:

mr.Active = false
while mr.Async and mr.AsyncProcessing do
   checkSynchronize()
end


EDIT:
Then added this to get the UI to update:
Code:

while mr.Async and mr.AsyncProcessing do
   checkSynchronize()
   MainForm.repaint()
end


Full Working Code (so far):
Code:

local NAME = 'I2 Cheat Engine Table State'
local CLASS_NAME = 'I2CETState'
local VERSION = '1.0.3'
local AUTHOR = 'Matt Irwin; [email protected]'
local LICENSE = [=[MIT License

Copyright (c) 2017 Matt Irwin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]=]

local format = string.format
local strE = string.empty or STRING_EMPTY or ''
local t = translate



I2CETState = {
   Name = NAME,
   ClassName = CLASS_NAME,
   Version = VERSION,
   Author = AUTHOR,
   License = LICENSE,
   DefaultState = 'default',
   SaveFileName = 'I2CETState.${StateName}.txt',
   UseMemoryRecordDescriptions = false,
   LineEnd = '\n',
   DisableBeforeLoad = true,
   PrintStatus = false, --true,
}



-- local Logger = {
--    LEVELS = {
--       OFF = 0,
--       FATAL = 1,
--       ERROR = 2,
--       WARN = 3,
--       INFO = 4,
--       DEBUG = 5,
--       TRACE = 6
--    },
-- }
-- for k, v in pairs(Logger.LEVELS) do
--    -- Logger[k:lower()] = function( ... ) return end
--    -- Logger[k:lower() .. 'f'] = function( ... ) return end
--    Logger[k:lower()] = function(msg, ex) return print(msg, ex) end
--    Logger[k:lower() .. 'f'] = function(msg, ... ) return print(string.format(msg, ... )) end
-- end
local Logger = Logger
if Logger == nil then
   if type(CETrequire) == 'function' then
      Logger = CETrequire('I2CETLogger')
   else
      Logger = require('I2CETLogger')
   end
   Logger.LogName = 'CETstate'
end


if type(CETrequire) == 'function' then
   I2CEHelpers = CETrequire('I2CEHelpers')
else
   I2CEHelpers = require('I2CEHelpers')
end


local function split(s, delimiter)
   return I2CEHelpers.split(s, delimiter)
end

local function interp(s, tbl)
   return I2CEHelpers.interp(s, tbl)
end



function I2CETState.saveTableState(stateName)
   Logger.trace()
   if not inMainThread() then
      return I2CETState.saveTableStateT(stateName)
   end
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.DefaultState
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   local fileStr = strE
   for i = 0, AddressList.Count - 1 do
      local mr = AddressList.getMemoryRecord(i)
      if mr.Description ~= '_[  I2CETState  ]_'
      and mr.Description:sub(0, 16) ~= 'Load Table State'
      and mr.Description:sub(0, 16) ~= 'Save Table State' then
         if mr.Active then
            local id = tostring(mr.ID)
            if I2CETState.UseMemoryRecordDescriptions then
               id = mr.Description
            end
            fileStr = fileStr .. id .. le
         end
      end
   end
   local f, err = io.open(fileName, 'w')
   if err then
      Logger.errorf('The file could not be opened, "%s", %s', fileName, err)
   elseif f and not err then
      f:write(fileStr)
      f:close()
   end
end

local function saveTableStateThread_logError(thread, fileName, err)
   Logger.errorf('The file could not be opened, "%s", %s', fileName, err)
   checkSynchronize()
   thread.terminate()
end

local function saveTableStateThread(thread, stateName)
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.DefaultState
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   local fileStr = strE
   for i = 0, AddressList.Count - 1 do
      local mr = AddressList.getMemoryRecord(i)
      if mr.Description ~= '_[  I2CETState  ]_'
      and mr.Description:sub(0, 16) ~= 'Load Table State'
      and mr.Description:sub(0, 16) ~= 'Save Table State' then
         if mr.Active then
            local id = tostring(mr.ID)
            if I2CETState.UseMemoryRecordDescriptions then
               id = mr.Description
            end
            fileStr = fileStr .. id .. le
         end
      end
   end
   local f, err = io.open(fileName, 'w')
   if err then
      synchronize(saveTableStateThread_logError, fileName, err)
   elseif f and not err then
      f:write(fileStr)
      f:close()
   end
   thread.terminate()
end

function I2CETState.saveTableStateT(stateName)
   Logger.trace()
   createThread(saveTableStateThread, stateName)
end



function I2CETState.loadTableState(stateName)
   Logger.trace()
   if not inMainThread() then
      return I2CETState.loadTableStateT(stateName)
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      getLuaEngine().show()
   end
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.DefaultState
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Setting Table State:  %s', stateName))
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   Logger.debugf('Using state file name: "%s"', fileName)
   local fileStr = nil
   local f, err = io.open(fileName, 'r')
   if err then
      Logger.infof('The local file could not be opened, "%s", %s', fileName, err)
      local tableFile = findTableFile(fileName)
      if tableFile == nil then
         Logger.warnf('file not found, "%s"', fileName)
         return
      end
      local stream = tableFile.getData()
      local bytes = stream.read(stream.Size)
      for i = 1, #bytes do
         if fileStr == nil then
            fileStr = strE
         end
         fileStr = fileStr .. string.char(bytes[i])
      end
   elseif f and not err then
      fileStr = f:read('*all')
      f:close()
   else
      Logger.errorf('The file could not be opened, "%s"', fileName)
   end
   if I2CETState.DisableBeforeLoad then
      for i = AddressList.Count - 1, 0, -1 do
         local mr = AddressList.getMemoryRecord(i)
         if mr.Description ~= '_[  I2CETState  ]_'
         and mr.Description:sub(0, 16) ~= 'Load Table State'
         and mr.Description:sub(0, 16) ~= 'Save Table State' then
            if mr.Active then
               Logger.infof('Disabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  print(format('Disabling:  %s', mr.Description))
               end
               mr.Active = false
               while mr.Async and mr.AsyncProcessing do
                  checkSynchronize()
               end
               sleep(0)
            end
         end
      end
      sleep(0)
   end
   if fileStr == nil then
      Logger.info('File string was nil')
      return
   end
   local lines = split(fileStr, I2CETState.LineEnd)
   for i, v in ipairs(lines) do
      if v ~= nil and v ~= strE then
         local mr = nil
         if I2CETState.UseMemoryRecordDescriptions then
            mr = AddressList.getMemoryRecordByDescription(v)
         else
            mr = AddressList.getMemoryRecordByID(tonumber(v))
         end
         if mr ~= nil then
            if not mr.Active then
               Logger.infof('Enabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  print(format('Enabling:  %s', mr.Description))
               end
               mr.Active = true
               while mr.Async and mr.AsyncProcessing do
                  checkSynchronize()
               end
               sleep(0)
            end
         end
      end
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Table State Set:  %s', stateName))
      getLuaEngine().hide()
   end
end


local function loadTableStateThreadSynced(thread, stateName)
   Logger.trace()
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      getLuaEngine().show()
   end
   local le = I2CETState.LineEnd
   if stateName == nil then
      stateName = I2CETState.CurrentStateName or I2CETState.DefaultState
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Setting Table State:  %s', stateName))
   end
   local fileName = interp(I2CETState.SaveFileName, { StateName = stateName } )
   Logger.debugf('Using state file name: "%s"', fileName)
   local fileStr = nil
   local f, err = io.open(fileName, 'r')
   if err then
      Logger.infof('The local file could not be opened, "%s", %s', fileName, err)
      local tableFile = findTableFile(fileName)
      if tableFile == nil then
         Logger.warnf('file not found, "%s"', fileName)
         return
      end
      local stream = tableFile.getData()
      local bytes = stream.read(stream.Size)
      for i = 1, #bytes do
         if fileStr == nil then
            fileStr = strE
         end
         fileStr = fileStr .. string.char(bytes[i])
      end
   elseif f and not err then
      fileStr = f:read('*all')
      f:close()
   else
      Logger.errorf('The file could not be opened, "%s"', fileName)
   end
   if I2CETState.DisableBeforeLoad then
      for i = AddressList.Count - 1, 0, -1 do
         local mr = AddressList.getMemoryRecord(i)
         if mr.Description ~= '_[  I2CETState  ]_'
         and mr.Description:sub(0, 16) ~= 'Load Table State'
         and mr.Description:sub(0, 16) ~= 'Save Table State' then
            if mr.Active then
               Logger.infof('Disabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  print(format('Disabling:  %s', mr.Description))
               end
               mr.Active = false
               while mr.Async and mr.AsyncProcessing do
                  checkSynchronize()
                  MainForm.repaint()
               end
               sleep(0)
            end
         end
      end
      sleep(0)
   end
   if fileStr == nil then
      Logger.info('File string was nil')
      return
   end
   local lines = split(fileStr, I2CETState.LineEnd)
   for i, v in ipairs(lines) do
      if v ~= nil and v ~= strE then
         local mr = nil
         if I2CETState.UseMemoryRecordDescriptions then
            mr = AddressList.getMemoryRecordByDescription(v)
         else
            mr = AddressList.getMemoryRecordByID(tonumber(v))
         end
         if mr ~= nil then
            if not mr.Active then
               Logger.infof('Enabling memory record: %d, %d, "%s"', mr.Index, mr.ID, mr.Description)
               if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
                  print(format('Enabling:  %s', mr.Description))
               end
               mr.Active = true
               while mr.Async and mr.AsyncProcessing do
                  checkSynchronize()
                  MainForm.repaint()
               end
               sleep(0)
            end
         end
      end
   end
   if I2CETState.PrintStatus and Logger.Level <= Logger.LEVELS.WARN then
      print(format('Table State Set:  %s', stateName))
      getLuaEngine().hide()
   end
   I2CETState.CurrentStateName = nil
   checkSynchronize()
   if thread ~= nil and type(thread.terminate) == 'function' then
      thread.terminate()
   end
end

local function loadTableStateThread(thread, stateName)
   synchronize(loadTableStateThreadSynced, stateName)
   thread.terminate()
end

function I2CETState.loadTableStateT(stateName)
   Logger.trace()
   I2CETState.CurrentStateName = stateName
   createThread(loadTableStateThread, stateName)
end


return I2CETState

_________________
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites