client.rb 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. require 'eventmachine'
  2. require 'tempfile'
  3. require 'fiber'
  4. module Woe
  5. class Minifilter
  6. def initialize(give)
  7. @give = give
  8. @fiber = nil
  9. end
  10. def wait_for_input(val=nil)
  11. return Fiber.yield(val)
  12. end
  13. def filter_input(line)
  14. if line =~ /2/
  15. return (line + line)
  16. end
  17. if line =~ /4/
  18. res = wait_for_input
  19. return (res * 4)
  20. end
  21. if line =~ /0/
  22. return (nil)
  23. end
  24. return line
  25. end
  26. end
  27. class Client < EventMachine::Connection
  28. attr_accessor :id
  29. attr_accessor :server
  30. def initialize(*args)
  31. super(*args)
  32. @id = nil
  33. @server = nil
  34. @connected = false
  35. @port = nil
  36. @ip = nil
  37. @fiber = nil
  38. @account = nil
  39. @filter = ::Woe::Minifilter.new(self)
  40. end
  41. def post_init()
  42. send_data("Welcome!\n")
  43. pn = self.get_peername
  44. @port, @ip = Socket.unpack_sockaddr_in(pn)
  45. send_data("You are connecting from #{@ip}:#{@port}\n")
  46. @connected = true
  47. self.send_data("Login:")
  48. end
  49. def save
  50. self.send_data("Saving...")
  51. do_save = proc do
  52. begin
  53. f = Tempfile.new('random')
  54. sleep 3
  55. f.write("I'm saving data.")
  56. ensure
  57. f.close
  58. end
  59. end
  60. on_save = proc do
  61. self.send_data("Saved.")
  62. end
  63. EM.defer(do_save, on_save)
  64. end
  65. # Basically, this method yields the fiber, and will return
  66. # with the input that will cme later when the fiber is resumed, normally
  67. # when more input becomes available from the client.
  68. # The
  69. def wait_for_input
  70. data = Fiber.yield
  71. # the filters MUST be aplied here, since then it can also be
  72. # fake-syncronous and use Fiber.yield to wait for additional input if
  73. # needed
  74. line = @filter.filter_input(data)
  75. return line
  76. end
  77. def try
  78. self.send_data("\nOK, let's try. What do you say?:")
  79. try = wait_for_input
  80. self.send_data("\nOK, nice try #{try}.\n")
  81. end
  82. # Fake synchronous handing of input
  83. def handle_input()
  84. @login = wait_for_input
  85. self.send_data("\nPassword for #{@login}:")
  86. @password = wait_for_input
  87. self.send_data("\nOK #{@password}, switching to command mode.\n")
  88. while @connected
  89. line = wait_for_input
  90. # If the user says 'quit', disconnect them
  91. if line =~ /^\/quit/
  92. @connected = false
  93. close_connection_after_writing
  94. # Shut down the server if we hear 'shutdown'
  95. elsif line =~ /^\/reload/
  96. @server.reload
  97. elsif line =~ /^\/shutdown/
  98. @connected = false
  99. @server.stop
  100. elsif line =~ /^\/save/
  101. self.save
  102. elsif line =~ /^\/try/
  103. self.try
  104. else
  105. @server.broadcast("Client #{id} says #{line}")
  106. end
  107. end
  108. end
  109. def receive_data(data)
  110. # Ignore any input if already requested disconnection
  111. return unless @connected
  112. #
  113. if @fiber
  114. @fiber.resume(data)
  115. else
  116. # set up a fiber to handle the input
  117. # Like that, the handle_input can be programmed in a fake-syncronous way
  118. @fiber = Fiber.new do
  119. handle_input()
  120. end
  121. # Must resume twice becaus of the way handle_input works
  122. @fiber.resume()
  123. @fiber.resume(data)
  124. end
  125. end
  126. def unbind
  127. $stderr.puts("Client #{id} has left")
  128. @server.disconnect(@id)
  129. end
  130. end
  131. end