ease_engine 0.0.19

Sign up to get free protection for your applications and to get access to all the features.
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