ease_engine 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +4 -0
  5. data/README.md +33 -0
  6. data/Rakefile +1 -0
  7. data/benchmark/echo/echo_client.rb +90 -0
  8. data/benchmark/echo/echo_packet.rb +9 -0
  9. data/benchmark/echo/echo_server.rb +48 -0
  10. data/benchmark/fps.rb +23 -0
  11. data/benchmark/http.rb +9 -0
  12. data/benchmark/loop/Makefile +39 -0
  13. data/benchmark/loop/Makefile.rb +11 -0
  14. data/benchmark/loop/Rakefile +42 -0
  15. data/benchmark/loop/loop.c +38 -0
  16. data/benchmark/loop/loop.cs +33 -0
  17. data/benchmark/loop/loop.go +33 -0
  18. data/benchmark/loop/loop.js +23 -0
  19. data/benchmark/loop/loop.php +22 -0
  20. data/benchmark/loop/loop.rb +19 -0
  21. data/benchmark/loop/loop.scala +30 -0
  22. data/benchmark/loop/loop_c +0 -0
  23. data/benchmark/loop/loop_cs +0 -0
  24. data/benchmark/loop/loop_go +0 -0
  25. data/benchmark/measure/measure_client.rb +76 -0
  26. data/benchmark/measure/measure_server.rb +47 -0
  27. data/benchmark/process.rb +3 -0
  28. data/benchmark/tcp/tcp_client.rb +45 -0
  29. data/benchmark/tcp/tcp_server.rb +40 -0
  30. data/benchmark/udp/udp_client.rb +84 -0
  31. data/benchmark/udp/udp_packet.rb +40 -0
  32. data/benchmark/udp/udp_server.rb +33 -0
  33. data/bin/console +14 -0
  34. data/bin/setup +7 -0
  35. data/ease_engine.gemspec +27 -0
  36. data/lib/ease_engine.rb +35 -0
  37. data/lib/ease_engine/application.rb +269 -0
  38. data/lib/ease_engine/buffer.rb +25 -0
  39. data/lib/ease_engine/data.rb +158 -0
  40. data/lib/ease_engine/frame.rb +38 -0
  41. data/lib/ease_engine/http.rb +42 -0
  42. data/lib/ease_engine/log.rb +109 -0
  43. data/lib/ease_engine/measure.rb +25 -0
  44. data/lib/ease_engine/packet.rb +150 -0
  45. data/lib/ease_engine/platform.rb +19 -0
  46. data/lib/ease_engine/process.rb +31 -0
  47. data/lib/ease_engine/socket.rb +174 -0
  48. data/lib/ease_engine/time.rb +22 -0
  49. data/lib/ease_engine/timer.rb +72 -0
  50. data/lib/ease_engine/version.rb +3 -0
  51. data/lib/ease_engine/watcher.rb +95 -0
  52. data/types/Rakefile +83 -0
  53. data/types/csharp/EaseEngine.cs +39 -0
  54. data/types/csharp/EaseEngine/Buffer.cs +49 -0
  55. data/types/csharp/EaseEngine/Measure.cs +47 -0
  56. data/types/csharp/EaseEngine/Time.cs +41 -0
  57. data/types/csharp/EaseEngine/Version.cs +7 -0
  58. data/types/csharp/Unity/Assets/Menu.cs +56 -0
  59. data/types/csharp/Unity/Assets/Menu.unity +0 -0
  60. metadata +158 -0
@@ -0,0 +1,38 @@
1
+ require "ease_engine/measure"
2
+
3
+ module EaseEngine
4
+ class Frame
5
+ @@fps = 0
6
+ @@measure = EaseEngine::Measure.new
7
+ @@offset_usec = 0 # sleep が意図した時間分 sleep するとは限らないので、遅延分を次の sleep 時間から引くためのオフセット値
8
+
9
+ def self.fps=( fps )
10
+ @@fps = fps
11
+ end
12
+
13
+ def self.fps
14
+ @@fps
15
+ end
16
+
17
+ def self.update( &block )
18
+ if 0 == @@fps
19
+ block.call( 0, 0.0 )
20
+ else
21
+ @@measure.check
22
+ sleep_time_usec = 1000000 / @@fps - @@measure.update_usec + @@offset_usec
23
+ sleep_time_usec = 0 if sleep_time_usec < 0
24
+
25
+ @@measure.start
26
+ @@offset_usec = sleep_time_usec
27
+ begin
28
+ measure = EaseEngine::Measure.new
29
+ sleep_time_f = sleep_time_usec.to_f / 1000000
30
+ block.call( sleep_time_usec, sleep_time_f )
31
+ sleep_time_usec -= measure.check
32
+ end while 0 < sleep_time_usec
33
+ @@offset_usec -= @@measure.check
34
+ @@measure.start
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,42 @@
1
+ require "net/http"
2
+
3
+ module EaseEngine
4
+ class HTTP
5
+ def self.parse( url )
6
+ uri = URI.parse url
7
+ uri
8
+ end
9
+
10
+ def self.is_ssl( uri )
11
+ "https" == uri.scheme
12
+ end
13
+
14
+ def self.get( url, params = {} )
15
+ uri = self.parse url
16
+ uri.query = URI.encode_www_form( params )
17
+ request = Net::HTTP::Get.new uri
18
+ http = Net::HTTP.new( uri.host, uri.port )
19
+ response = http.start{|http|
20
+ http.use_ssl = self.is_ssl( uri )
21
+ http.request request
22
+ }
23
+
24
+ case response
25
+ when Net::HTTPRedirection
26
+ response = self.get response[ "location" ]
27
+ end
28
+ response
29
+ end
30
+
31
+ def self.post( url, params = {} )
32
+ uri = self.parse url
33
+ request = Net::HTTP::Post.new uri
34
+ request.set_form_data( params )
35
+ response = Net::HTTP.start( uri.host, uri.port ){|http|
36
+ http.use_ssl = self.is_ssl( uri )
37
+ http.request request
38
+ }
39
+ response
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,109 @@
1
+ require "ease_engine/time"
2
+ require "fileutils"
3
+
4
+ module EaseEngine
5
+ module Log
6
+ FLAG_DBG = 0x1
7
+ FLAG_INF = 0x2
8
+ FLAG_WRN = 0x4
9
+ FLAG_ERR = 0x8
10
+ FLAG_ALL = FLAG_DBG | FLAG_INF | FLAG_WRN | FLAG_ERR
11
+
12
+ class IO
13
+ attr_accessor :file, :flags
14
+
15
+ def initialize( file, flags = FLAG_ALL )
16
+ @file = file
17
+ @flags = flags
18
+ end
19
+
20
+ def write( time, msg )
21
+ @file.puts "#{time} #{msg}"
22
+ @file.flush
23
+ end
24
+
25
+ def close
26
+ @file.close
27
+ end
28
+ end
29
+
30
+ class RotateFile < EaseEngine::Log::IO
31
+ attr_accessor :max_size
32
+
33
+ def initialize( file, flags = FLAG_ALL, max_size = 1024 * 1024 )
34
+ super( file, flags )
35
+
36
+ @max_size = max_size
37
+ end
38
+
39
+ def rotate( file_path )
40
+ @file.rewind
41
+ FileUtils.copy_stream( @file, file_path )
42
+ @file.truncate( 0 )
43
+ end
44
+ end
45
+
46
+ class DailyRotateFile < RotateFile
47
+ def initialize( *args )
48
+ super( *args )
49
+
50
+ @check_time = EaseEngine::Time.new
51
+ end
52
+
53
+ def write( time, msg )
54
+ on_daily( time ) if is_daily( time )
55
+
56
+ super( time, msg )
57
+ end
58
+
59
+ def is_daily( time )
60
+ ! @check_time.is_same_day( time ) && @max_size <= @file.size
61
+ end
62
+
63
+ def on_daily( time )
64
+ rotate( sprintf( "%s_%04d%02d%02d", @file.path, @check_time.year, @check_time.month, @check_time.day ) )
65
+ @check_time = time
66
+ end
67
+ end
68
+
69
+ @@files = [ Log::IO.new( $stdout ) ]
70
+
71
+ def self.files=( files )
72
+ @@files = files
73
+ end
74
+
75
+ def self.files
76
+ @@files
77
+ end
78
+
79
+ def self.output( flags, msg )
80
+ time = EaseEngine::Time.new
81
+ @@files.each{|io|
82
+ next if 0 == flags & io.flags
83
+
84
+ io.write( time, msg )
85
+ }
86
+ end
87
+
88
+ def self.dbg( msg )
89
+ self.output( FLAG_DBG, "DBG #{msg}" )
90
+ end
91
+
92
+ def self.inf( msg )
93
+ self.output( FLAG_INF, "INF #{msg}" )
94
+ end
95
+
96
+ def self.wrn( msg )
97
+ self.output( FLAG_WRN, "WRN #{msg}" )
98
+ end
99
+
100
+ def self.err( msg )
101
+ self.output( FLAG_ERR, "ERR #{msg}" )
102
+ end
103
+ end
104
+ end
105
+
106
+ EE_LOG_DBG = Proc.new{|msg| EaseEngine::Log.dbg( "#{msg} #{EaseEngine::trace_info( 2 )[ :str ]}" ) }
107
+ EE_LOG_INF = Proc.new{|msg| EaseEngine::Log.inf( "#{msg} #{EaseEngine::trace_info( 2 )[ :str ]}" ) }
108
+ EE_LOG_WRN = Proc.new{|msg| EaseEngine::Log.wrn( "#{msg} #{EaseEngine::trace_info( 2 )[ :str ]}" ) }
109
+ EE_LOG_ERR = Proc.new{|msg| EaseEngine::Log.err( "#{msg} #{EaseEngine::trace_info( 2 )[ :str ]}" ) }
@@ -0,0 +1,25 @@
1
+ require "ease_engine/time"
2
+
3
+ module EaseEngine
4
+ class Measure
5
+ attr_accessor :start_time, :end_time, :update_usec, :count
6
+
7
+ def initialize
8
+ start
9
+ end
10
+
11
+ def start
12
+ @start_time = @end_time = EaseEngine::Time.new
13
+ @update_usec = 0
14
+ @count = 0
15
+ end
16
+
17
+ def check
18
+ @end_time = EaseEngine::Time.new
19
+ @update_usec += ( @end_time - @start_time )
20
+ @count += 1
21
+ @start_time = @end_time
22
+ @update_usec
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,150 @@
1
+ require "ease_engine/socket"
2
+ require "msgpack"
3
+
4
+ module EaseEngine
5
+ class Packet
6
+ def self.pack_uint8( value )
7
+ [ value ].pack( "C" )
8
+ end
9
+
10
+ def self.pack_uint16( value )
11
+ [ value ].pack( "v" )
12
+ end
13
+
14
+ def self.pack_uint32( value )
15
+ [ value ].pack( "V" )
16
+ end
17
+
18
+ def self.pack_str( value )
19
+ [ value ].pack( "Z*" )
20
+ end
21
+
22
+ def self.unpack_uint8( buf )
23
+ ( 1 != buf.length ) ? 0 : buf.unpack( "C" )[ 0 ]
24
+ end
25
+
26
+ def self.unpack_uint16( buf )
27
+ ( 2 != buf.length ) ? 0 : buf.unpack( "v" )[ 0 ]
28
+ end
29
+
30
+ def self.unpack_uint32( buf )
31
+ ( 4 != buf.length ) ? 0 : buf.unpack( "V" )[ 0 ]
32
+ end
33
+
34
+ def self.unpack_str( buf )
35
+ buf.unpack( "Z*" )[ 0 ]
36
+ end
37
+
38
+ def self.creates( socket )
39
+ packets = []
40
+
41
+ if ! socket.recv( socket.read_max_size ).empty?
42
+ while true
43
+ packet = self.create( socket )
44
+ break if packet.nil?
45
+
46
+ packets.push packet
47
+ end
48
+ end
49
+
50
+ packets
51
+ end
52
+
53
+ def self.create( socket )
54
+ # 受信したデータから動的にパケットクラスを生成
55
+ begin
56
+ size = self.unpack_uint32( socket.read_buf.value.slice( 0, 4 ) )
57
+ break if ! socket.err.nil?
58
+ break if 0 == size
59
+ break if socket.read_buf.size < 4 + size
60
+
61
+ socket.read_buf >> 4
62
+ buf = socket.read_buf >> size
63
+ packet_name = self.unpack_str( buf )
64
+ buf.slice!( 0, packet_name.length + 1 )
65
+
66
+ packet = const_get( packet_name.gsub( /\./, "::" ) ).new
67
+ packet.unpack( buf )
68
+ rescue => err
69
+ EaseEngine::Log.err( "#{packet_name}: #{err}" )
70
+ socket.err = err
71
+ socket.is_disable = true
72
+ packet = nil
73
+ end while false
74
+
75
+ packet
76
+ end
77
+
78
+ attr_reader :packet_name
79
+
80
+ def initialize
81
+ @packers = []
82
+ @packet_name = self.class.name.gsub( /::/, "." )
83
+ end
84
+
85
+ def packer( *args )
86
+ args.each{|name|
87
+ self.class.class_eval <<-EOS
88
+ def #{name}
89
+ @#{name}
90
+ end
91
+
92
+ def #{name}=( value )
93
+ @#{name} = value
94
+ end
95
+ EOS
96
+
97
+ @packers.push name
98
+ }
99
+ end
100
+
101
+ def pack
102
+ bufs = []
103
+ bufs.push Packet.pack_str( @packet_name )
104
+ hash = {}
105
+ @packers.each{|name|
106
+ hash[ name.to_s ] = instance_variable_get( "@#{name}" )
107
+ }
108
+ bufs.push MessagePack.pack( hash )
109
+ bufs.join
110
+ end
111
+
112
+ def unpack( buf )
113
+ MessagePack.unpack( buf ).each{|name, value|
114
+ instance_variable_set( "@#{name}", value ) if @packers.include?( name.to_sym )
115
+ }
116
+ end
117
+
118
+ def write( socket, flags, *args )
119
+ packed_packet = pack
120
+ size = Packet.pack_uint32( packed_packet.size )
121
+ socket.send( "#{size}#{packed_packet}", flags, *args )
122
+ end
123
+ end
124
+
125
+ class MeasurePacket < Packet
126
+ def initialize
127
+ super
128
+
129
+ packer :sec, :usec
130
+
131
+ start
132
+ end
133
+
134
+ def start
135
+ time = ::Time.new
136
+ @sec = time.to_i
137
+ @usec = time.usec
138
+ end
139
+
140
+ def check
141
+ EaseEngine::Time.new - ::Time.at( @sec, @usec )
142
+ end
143
+ end
144
+
145
+ class HeartbeatBeginPacket < Packet
146
+ end
147
+
148
+ class HeartbeatEndPacket < Packet
149
+ end
150
+ end
@@ -0,0 +1,19 @@
1
+ module EaseEngine
2
+ class Platform
3
+ def self.ruby_platform
4
+ RUBY_PLATFORM.downcase
5
+ end
6
+
7
+ def self.mac?
8
+ self.ruby_platform =~ /darwin/
9
+ end
10
+
11
+ def self.linux?
12
+ self.ruby_platform =~ /linux/
13
+ end
14
+
15
+ def self.windows?
16
+ self.ruby_platform =~ /mingw/
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ require "open3"
2
+
3
+ module EaseEngine
4
+ class Process
5
+ def self.execute( cmd, option = {} )
6
+ out, err, status = Open3.capture3( cmd, option )
7
+ [ status.exitstatus, out, err ]
8
+ end
9
+
10
+ def self.daemon( path, is_daemon, *args, &block )
11
+ pid_file_path = "#{path}.pid"
12
+ begin
13
+ if File.exist?( pid_file_path )
14
+ EaseEngine::Log.err "PID file exist: #{pid_file_path}"
15
+ break
16
+ end
17
+
18
+ ::Process.daemon( *args ) if is_daemon
19
+ File.open( pid_file_path, "wb" ){|file|
20
+ file.puts "#{::Process.pid}"
21
+ }
22
+ log_file = File.open( "#{path}.log", "a+b" )
23
+ if ! log_file.nil?
24
+ block.call( log_file, pid_file_path )
25
+ log_file.close
26
+ end
27
+ File.delete pid_file_path if File.exist?( pid_file_path )
28
+ end while false
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,174 @@
1
+ require "ease_engine/buffer"
2
+ require "socket"
3
+
4
+ module EaseEngine
5
+ class Socket
6
+ attr_accessor :socket, :read_buf, :write_buf, :err
7
+ attr_accessor :read_max_size
8
+ attr_accessor :dst_addr
9
+ attr_accessor :is_disable
10
+
11
+ def initialize( *args )
12
+ @read_buf = EaseEngine::Buffer.new
13
+ @write_buf = EaseEngine::Buffer.new
14
+ @err = nil
15
+ @read_max_size = 2048
16
+ @dst_addr = nil
17
+ @is_disable = false
18
+ end
19
+
20
+ def bind( host, port )
21
+ @socket.bind( host, port )
22
+ end
23
+
24
+ def send( msg, flags, *args )
25
+ @write_buf << msg
26
+ @err = nil
27
+ begin
28
+ result = @socket.send( @write_buf.value, flags, *args )
29
+ rescue => err
30
+ @err = err
31
+ result = 0
32
+ end
33
+
34
+ @write_buf >> result
35
+ @is_disable = true if 0 == result
36
+
37
+ result
38
+ end
39
+
40
+ def recv( max, flags = 0 )
41
+ # 1回で全てのデータを読み込めるとは限らないので、複数回に分けてデータを全て読み込む
42
+ @err = nil
43
+ bufs = []
44
+ while true
45
+ begin
46
+ buf = @socket.recv_nonblock( max, flags )
47
+ rescue IO::EAGAINWaitReadable => err
48
+ buf = ""
49
+ rescue Errno::ECONNRESET => err
50
+ buf = ""
51
+ rescue => err
52
+ @err = err
53
+ buf = ""
54
+ end
55
+ break if buf.empty?
56
+
57
+ bufs.push buf
58
+ end
59
+
60
+ buf = bufs.join
61
+ @read_buf << buf
62
+ @is_disable = true if bufs.empty?
63
+
64
+ buf
65
+ end
66
+
67
+ def recvfrom( max, flags = 0 )
68
+ # 最大サイズが決まっているので、1回で読み込む
69
+ @err = nil
70
+ begin
71
+ result = @socket.recvfrom_nonblock( max, flags )
72
+ rescue => err
73
+ @err = err
74
+ result = [ "", nil ]
75
+ end
76
+
77
+ @read_buf << result[ 0 ]
78
+
79
+ result
80
+ end
81
+
82
+ def close
83
+ if ! @socket.nil?
84
+ @socket.close
85
+ @socket = nil
86
+ end
87
+ end
88
+
89
+ def to_i
90
+ @socket.to_i
91
+ end
92
+
93
+ def errno
94
+ @socket.getsockopt( ::Socket::SOL_SOCKET, ::Socket::SO_ERROR ).unpack( "i" )[ 0 ]
95
+ end
96
+ end
97
+
98
+ class TCPSocket < EaseEngine::Socket
99
+ attr_accessor :is_heartbeat
100
+ attr_accessor :is_timeout
101
+
102
+ def initialize( *args )
103
+ super
104
+
105
+ @is_heartbeat = false
106
+ @is_timeout = false
107
+
108
+ case args.length
109
+ when 1
110
+ @socket = args[ 0 ]
111
+ when 2
112
+ @dst_addr = ::TCPSocket.gethostbyname( args[ 0 ] )
113
+ @dst_addr.push args[ 1 ]
114
+ @socket = ::Socket.new( @dst_addr[ 2 ], ::Socket::SOCK_STREAM, 0 )
115
+ connect
116
+ end
117
+ end
118
+
119
+ def connect
120
+ @err = nil
121
+ begin
122
+ @socket.connect_nonblock( ::Socket.sockaddr_in( @dst_addr.last, @dst_addr[ 3 ] ) )
123
+ rescue => err
124
+ @err = err
125
+ end
126
+ end
127
+
128
+ def recv( max, flags = 0 )
129
+ @is_timeout = false
130
+ super( max, flags )
131
+ end
132
+ end
133
+
134
+ class TCPServer < EaseEngine::TCPSocket
135
+ def initialize( host, port )
136
+ super( ::TCPServer.new( host, port ) )
137
+ end
138
+
139
+ def listen( backlog = Socket::Constants::SOMAXCONN )
140
+ @socket.listen( backlog )
141
+ end
142
+
143
+ def accept
144
+ @err = nil
145
+ begin
146
+ EaseEngine::TCPSocket.new( @socket.accept )
147
+ rescue => err
148
+ @err = err
149
+ nil
150
+ end
151
+ end
152
+ end
153
+
154
+ class UDPSocket < EaseEngine::Socket
155
+ def initialize( *args )
156
+ super
157
+
158
+ @socket = ::UDPSocket.new
159
+ end
160
+
161
+ def send( msg, flags, *args )
162
+ if args.empty? && ! @dst_addr.nil?
163
+ super( msg, flags, @dst_addr[ 3 ], @dst_addr[ 1 ] )
164
+ else
165
+ super( msg, flags, *args )
166
+ end
167
+ end
168
+
169
+ def recv( max, flags = 0 )
170
+ buf, @dst_addr = recvfrom( max, flags )
171
+ buf
172
+ end
173
+ end
174
+ end