client.rb 3.5 KB

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