frostbitten 0.1.0

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.
@@ -0,0 +1,17 @@
1
+ #include "frostbitten.h"
2
+ #include "word.h"
3
+ #include "header.h"
4
+
5
+ #ifndef message_h
6
+ #define message_h
7
+ void message_init();
8
+ extern VALUE c_frostbitten_message;
9
+ // Frostbitten::Message
10
+ typedef struct _fb_message {
11
+ VALUE header; // Frostbitten::Header
12
+ VALUE words; // Array
13
+ } fb_message;
14
+ void frostbitten_message_deallocate(fb_message *message);
15
+ VALUE frostbitten_message_allocate (VALUE klass);
16
+ char** frostbitten_message_generate(fb_message *message, uint32_t *message_size);
17
+ #endif
@@ -0,0 +1,15 @@
1
+ // Include the Ruby headers and goodies
2
+ #include "frostbitten.h"
3
+ #include "word.h"
4
+ #include "header.h"
5
+ #include "message.h"
6
+
7
+
8
+ VALUE m_frostbitten;
9
+ // // The initialization method for this module
10
+ void Init_native() {
11
+ m_frostbitten = rb_define_module("Frostbitten");
12
+ header_init();
13
+ message_init();
14
+ word_init();
15
+ }
@@ -0,0 +1,20 @@
1
+ #include "packet.h"
2
+
3
+ fb_packet_buffer* read_buffer_from_io(FILE *fp) {
4
+ fb_packet_buffer *buffer = malloc(sizeof(fb_packet_buffer));
5
+ fread((void*)&buffer->header, sizeof(uint32_t), 1, fp);
6
+ fread((void*)&buffer->packetSize, sizeof(uint32_t), 1, fp);
7
+ fread((void*)&buffer->wordCount, sizeof(uint32_t), 1, fp);
8
+ return buffer;
9
+ }
10
+
11
+ bool is_packet_valid(fb_packet_buffer *buffer) {
12
+ if ( !&buffer->header || !&buffer->packetSize ) {
13
+ return false;
14
+ }
15
+
16
+ if ( buffer->packetSize > 16384 ) {
17
+ return false;
18
+ }
19
+ return true;
20
+ }
@@ -0,0 +1,11 @@
1
+ #include "frostbitten.h"
2
+ #ifndef packet_h
3
+ #define packet_h
4
+ typedef struct _fb_packet_buffer {
5
+ uint32_t header;
6
+ uint32_t packetSize;
7
+ uint32_t wordCount;
8
+ } fb_packet_buffer;
9
+ fb_packet_buffer* read_buffer_from_io(FILE *fp);
10
+ bool is_packet_valid(fb_packet_buffer *buffer);
11
+ #endif
@@ -0,0 +1,60 @@
1
+ #include "word.h"
2
+ VALUE c_frostbitten_word;
3
+ void frostbitten_word_deallocate(fb_word *word) {
4
+ word->len = 0;
5
+ memset(&word->str[0],0,sizeof(&word->str));
6
+ word->term = 0x0;
7
+ ruby_xfree(word);
8
+ }
9
+
10
+ VALUE frostbitten_word_read_from_io(VALUE self, VALUE io) {
11
+ fb_word *word;
12
+ Data_Get_Struct(self, fb_word, word);
13
+
14
+ rb_io_t *fptr;
15
+ GetOpenFile(io, fptr);
16
+ rb_io_check_readable(fptr);
17
+
18
+ FILE *fp = rb_io_stdio_file(fptr);
19
+ fread((void*)&word->len, sizeof(uint32_t),1, fp);
20
+ fread((void*)&word->str, sizeof(char),word->len, fp);
21
+ fread((void*)&word->term, sizeof(char), 1, fp);
22
+ printf("%s", word->str);
23
+ return self;
24
+ }
25
+
26
+ VALUE frostbitten_word_cls_read_from_io(VALUE self, VALUE io) {
27
+ return self;
28
+ }
29
+
30
+ VALUE frostbitten_word_write_to_io(VALUE self, VALUE io) {
31
+ fb_word *word;
32
+ Data_Get_Struct(self, fb_word, word);
33
+
34
+ rb_io_t *fptr;
35
+ GetOpenFile(io, fptr);
36
+ rb_io_check_writable(fptr);
37
+
38
+ FILE *fp = rb_io_stdio_file(fptr);
39
+ // fwrite((const void*)&val,sizeof(uint32_t),1,fp);
40
+
41
+ rb_io_bufwrite(io, (const void*)&word->len, sizeof(uint32_t));
42
+ rb_io_bufwrite(io, (const void*)&word->str, sizeof(uint32_t));
43
+ rb_io_bufwrite(io, (const void*)&word->len, sizeof(uint32_t));
44
+ return self;
45
+ }
46
+
47
+ VALUE frostbitten_word_allocate (VALUE klass) {
48
+ fb_word *word = ruby_xmalloc(sizeof(fb_word));
49
+ return Data_Wrap_Struct(klass, NULL, frostbitten_word_deallocate, word);
50
+ }
51
+
52
+ void word_init() {
53
+ c_frostbitten_word = rb_define_class_under(m_frostbitten, "Word", rb_cString);
54
+ rb_define_alloc_func(c_frostbitten_word, frostbitten_word_allocate);
55
+
56
+ rb_define_method(c_frostbitten_word, "read", frostbitten_word_read_from_io, 1);
57
+ rb_define_method(c_frostbitten_word, "write", frostbitten_word_write_to_io, 1);
58
+ rb_define_singleton_method(c_frostbitten_word, "read", frostbitten_word_cls_read_from_io, 1);
59
+
60
+ }
@@ -0,0 +1,17 @@
1
+ #include "frostbitten.h"
2
+
3
+ #ifndef word_h
4
+ #define word_h
5
+
6
+ extern VALUE c_frostbitten_word;
7
+ void word_init();
8
+ typedef struct _fb_word {
9
+ uint32_t len;
10
+ char str[INT_MAX];
11
+ char term;
12
+ } fb_word;
13
+
14
+ void frostbitten_word_deallocate(fb_word *word);
15
+ VALUE frostbitten_word_allocate (VALUE klass);
16
+
17
+ #endif
@@ -0,0 +1,106 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "frostbitten"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Emil Palm"]
12
+ s.date = "2013-02-22"
13
+ s.description = "Gem that provides RCON commands for frostbite 2 engine games. BF3, BFBC2 and so on"
14
+ s.email = "emil.palm@x86.nu"
15
+ s.extensions = ["ext/frostbitten/extconf.rb"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "Guardfile",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "ext/.DS_Store",
30
+ "ext/frostbitten/.DS_Store",
31
+ "ext/frostbitten/Makefile",
32
+ "ext/frostbitten/extconf.rb",
33
+ "ext/frostbitten/frostbitten.h",
34
+ "ext/frostbitten/header.c",
35
+ "ext/frostbitten/header.h",
36
+ "ext/frostbitten/message.c",
37
+ "ext/frostbitten/message.h",
38
+ "ext/frostbitten/native.c",
39
+ "ext/frostbitten/packet.c",
40
+ "ext/frostbitten/packet.h",
41
+ "ext/frostbitten/word.c",
42
+ "ext/frostbitten/word.h",
43
+ "frostbitten.gemspec",
44
+ "lib/frostbitten.rb",
45
+ "lib/frostbitten/client.rb",
46
+ "lib/frostbitten/connection.rb",
47
+ "lib/frostbitten/map.rb",
48
+ "lib/frostbitten/methods/admin.rb",
49
+ "lib/frostbitten/methods/normal.rb",
50
+ "lib/frostbitten/player.rb",
51
+ "lib/frostbitten/score.rb",
52
+ "lib/frostbitten/server.rb",
53
+ "lib/frostbitten/version.rb",
54
+ "spec/frostbitten_spec.rb",
55
+ "spec/lib/frostbitten_client_spec.rb",
56
+ "spec/lib/frostbitten_server_spec.rb",
57
+ "spec/spec_helper.rb",
58
+ "test/helper.rb",
59
+ "test/test_frostbitten.rb"
60
+ ]
61
+ s.homepage = "http://github.com/mrevilme/frostbitten"
62
+ s.licenses = ["MIT"]
63
+ s.require_paths = ["lib"]
64
+ s.rubygems_version = "1.8.24"
65
+ s.summary = "Gem that provides RCON commands for frostbite 2 engine games. BF3, BFBC2 and so on"
66
+
67
+ if s.respond_to? :specification_version then
68
+ s.specification_version = 3
69
+
70
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
71
+ s.add_runtime_dependency(%q<hashie>, [">= 0"])
72
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
73
+ s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
74
+ s.add_development_dependency(%q<bundler>, [">= 0"])
75
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
76
+ s.add_development_dependency(%q<rspec>, [">= 0"])
77
+ s.add_development_dependency(%q<rb-fsevent>, [">= 0"])
78
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
79
+ s.add_development_dependency(%q<terminal-notifier-guard>, [">= 0"])
80
+ s.add_development_dependency(%q<rake-compiler>, [">= 0"])
81
+ else
82
+ s.add_dependency(%q<hashie>, [">= 0"])
83
+ s.add_dependency(%q<shoulda>, [">= 0"])
84
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
85
+ s.add_dependency(%q<bundler>, [">= 0"])
86
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
87
+ s.add_dependency(%q<rspec>, [">= 0"])
88
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
89
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
90
+ s.add_dependency(%q<terminal-notifier-guard>, [">= 0"])
91
+ s.add_dependency(%q<rake-compiler>, [">= 0"])
92
+ end
93
+ else
94
+ s.add_dependency(%q<hashie>, [">= 0"])
95
+ s.add_dependency(%q<shoulda>, [">= 0"])
96
+ s.add_dependency(%q<rdoc>, ["~> 3.12"])
97
+ s.add_dependency(%q<bundler>, [">= 0"])
98
+ s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
99
+ s.add_dependency(%q<rspec>, [">= 0"])
100
+ s.add_dependency(%q<rb-fsevent>, [">= 0"])
101
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
102
+ s.add_dependency(%q<terminal-notifier-guard>, [">= 0"])
103
+ s.add_dependency(%q<rake-compiler>, [">= 0"])
104
+ end
105
+ end
106
+
@@ -0,0 +1,37 @@
1
+ require 'frostbitten/native'
2
+ require 'frostbitten/version'
3
+ require 'frostbitten/connection'
4
+ require 'frostbitten/client'
5
+ require 'frostbitten/player'
6
+ require 'frostbitten/server'
7
+ require 'frostbitten/score'
8
+ require 'frostbitten/map'
9
+
10
+ # Author:: Emil Palm (mailto:emil@x86.nu)
11
+ # Copyright:: Copyright (c) 2013 Emil Palm
12
+ # License:: Distributes under the same terms as Ruby
13
+
14
+ # This module will give you a shorthand to a client object.
15
+ # very useful if your only connecting to one server
16
+
17
+ module Frostbitten
18
+ # Setup the module with a URI 'fbrcon://password@ip:port'
19
+ # Also accepts options { :keepalive = true/false, :socket_timeout => 1 }
20
+ def self.setup(uri,options={})
21
+ @client = Frostbitten::Client.new(uri,options)
22
+ end
23
+
24
+ # Method to return the client object if raw access is needed
25
+ def self.conn
26
+ return @client
27
+ end
28
+
29
+ # :nodoc:
30
+ def self.method_missing(meth, *args, &block)
31
+ if @client and @client.respond_to?(meth)
32
+ @client.public_send(meth, *args, &block)
33
+ else
34
+ super
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ require 'digest/md5'
2
+ require 'frostbitten/methods/admin'
3
+ require 'frostbitten/methods/normal'
4
+
5
+ # Author:: Emil Palm (mailto:emil@x86.nu)
6
+ # Copyright:: Copyright (c) 2013 Emil Palm
7
+ # License:: Distributes under the same terms as Ruby
8
+
9
+ # This class will give you a shorthand to a Server object.
10
+ # and give you a easy way to execute methods on server
11
+ # that are predefined in this class.
12
+
13
+ module Frostbitten
14
+ class Client
15
+ # Access to the server object.
16
+ attr_accessor :connection
17
+
18
+ # Initialize class with URI 'fbrcon://password@ip:port'
19
+ # Also accept options
20
+ # { :keepalive = true/false, :socket_timeout => 1 }
21
+ # will setup connection object.
22
+ def initialize(uri=nil, options={})
23
+ if uri
24
+ self.connection = Frostbitten::Connection.new(uri,options)
25
+ end
26
+ end
27
+
28
+ # Will create Frostbitten::Header and Frostbitten::Message
29
+ # objects for given commands.
30
+ # Accepts either a string or a array of strings to send.
31
+ # Returns a list of words returned by server.
32
+ def send(commands)
33
+ return unless self.connection
34
+
35
+ if commands.is_a? String
36
+ commands = [commands]
37
+ end
38
+
39
+ header = Frostbitten::Header.new(:origin => :client, :response => false)
40
+ msg = Frostbitten::Message.new(:words => commands, :header => header)
41
+ message = connection.send(msg)
42
+ return message.words[1..-1]
43
+ end
44
+
45
+ include Frostbitten::Methods::AdminCommands
46
+ include Frostbitten::Methods::NormalCommands
47
+ # :include:methods/admin.rb
48
+ # :include:methods/normal.rb
49
+ end
50
+ end
@@ -0,0 +1,100 @@
1
+ require 'uri/generic'
2
+ require 'socket'
3
+ module URI
4
+ class FrostbiteRCON < Generic
5
+ COMPONENT = [:scheme,:host,:port]
6
+ end
7
+ @@schemes['fbrcon'] = FrostbiteRCON
8
+ end
9
+
10
+ module Frostbitten
11
+ class Connection
12
+ DEFAULTS = {
13
+ # connect/read/write timeout for socket operations
14
+ :socket_timeout => 0.5,
15
+
16
+ :keepalive => true
17
+ }
18
+
19
+ attr_accessor :hostname, :port, :password
20
+ attr_reader :sock, :sequence, :timeout
21
+
22
+ # Initialize class with URI 'fbrcon://password@ip:port'
23
+ # Also accept options
24
+ # { :keepalive = true/false, :socket_timeout => 1 }
25
+
26
+ def initialize(uri, options={})
27
+ server_parsed = URI.parse(uri)
28
+ self.hostname = server_parsed.host
29
+ self.password = server_parsed.user
30
+ self.port = server_parsed.port ||= 47200
31
+
32
+ @sock = nil
33
+ @sequence = options[:sequence] ||= 0
34
+
35
+ @options = DEFAULTS.merge(options)
36
+ end
37
+
38
+ # Returns if the socket is alive of closed
39
+ def alive?
40
+ return true if @sock
41
+ return false
42
+ end
43
+
44
+ # Connects socket
45
+ def connect
46
+ return if alive?
47
+ @sock = TCPSocket.open(self.hostname, self.port);
48
+ if @options[:socket_timeout]
49
+ secs = Integer(@options[:socket_timeout])
50
+ usecs = Integer((@options[:socket_timeout] - secs) * 1_000_000)
51
+ optval = [secs, usecs].pack("l_2")
52
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
53
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
54
+ end
55
+
56
+ if @options[:keepalive]
57
+ @sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
58
+ end
59
+ end
60
+
61
+ # Close socket
62
+ def close
63
+ return unless alive?
64
+ @sock.close
65
+ @sock = nil
66
+ end
67
+
68
+
69
+ # Accepts a Frostbitten::Mesage and sends it over socket
70
+ # updates message.header.sequence with a incremented version
71
+ # of server sequence counter.
72
+ # can raise NameError, ArgumentError or StandardError based on return data
73
+ # from server.
74
+ def send(message)
75
+ connect unless self.alive?
76
+ unless message.header.is_response?
77
+ @sequence += 1
78
+ end
79
+
80
+ message.header.sequence = self.sequence
81
+ message.write @sock
82
+
83
+ m = Message.new()
84
+ m.read @sock
85
+
86
+ unless m.words.first == "OK"
87
+ m.words.first.tap do |word|
88
+ if word == "UnknownCommand"
89
+ raise NameError, "UnknownCommand raised by server"
90
+ elsif word == "InvalidArguments"
91
+ raise ArgumentError, "InvalidArguments raised by server"
92
+ else
93
+ raise StandardError, "Server returned #{word}"
94
+ end
95
+ end
96
+ end
97
+ return m
98
+ end
99
+ end
100
+ end