weskit 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +2 -0
- data/README +2 -0
- data/Rakefile +1 -0
- data/lib/weskit.rb +3 -0
- data/lib/weskit/lobby.rb +11 -0
- data/lib/weskit/lobby/bot.rb +85 -0
- data/lib/weskit/lobby/bot_error.rb +3 -0
- data/lib/weskit/version.rb +3 -0
- data/lib/weskit/wml.rb +11 -0
- data/lib/weskit/wml/node.rb +76 -0
- data/lib/weskit/wml/node_array.rb +25 -0
- data/lib/weskit/wml/node_error.rb +3 -0
- data/lib/weskit/wml/reader.rb +67 -0
- data/lib/weskit/wml/reader_error.rb +3 -0
- data/lib/weskit/wml/string_extensions.rb +59 -0
- data/weskit.gemspec +24 -0
- metadata +82 -0
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/weskit.rb
ADDED
data/lib/weskit/lobby.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'weskit/wml'
|
2
|
+
|
3
|
+
module Weskit::Lobby
|
4
|
+
require 'socket'
|
5
|
+
require 'stringio'
|
6
|
+
require 'zlib'
|
7
|
+
|
8
|
+
class Bot
|
9
|
+
def initialize nickname, options = {}
|
10
|
+
defaults = {
|
11
|
+
:debug => false,
|
12
|
+
:port => 15000,
|
13
|
+
:server => 'server.wesnoth.org',
|
14
|
+
:version => '1.10.0'
|
15
|
+
}
|
16
|
+
options = defaults.merge options
|
17
|
+
|
18
|
+
@server = options[:server]
|
19
|
+
@port = options[:port]
|
20
|
+
@version = options[:version]
|
21
|
+
|
22
|
+
@reader = Weskit::WML::Reader.new
|
23
|
+
@nickname = nickname
|
24
|
+
end
|
25
|
+
|
26
|
+
def connect_and &operations
|
27
|
+
@socket = TCPSocket.new @server, @port
|
28
|
+
basic_communication
|
29
|
+
response = read_wml
|
30
|
+
@socket.close
|
31
|
+
|
32
|
+
@socket = TCPSocket.new response.redirect[:host], response.redirect[:port]
|
33
|
+
basic_communication
|
34
|
+
|
35
|
+
raise "Server send node other than 'mustlogin'" if read_wml.mustlogin.nil?
|
36
|
+
send_wml 'login', :selective_ping => 1, :username => @nickname
|
37
|
+
raise "Server send node other than 'join_lobby'" if read_wml.join_lobby.nil?
|
38
|
+
|
39
|
+
result = yield self
|
40
|
+
|
41
|
+
sleep 3
|
42
|
+
@socket.close
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
def read_wml
|
47
|
+
wml = @reader.read_str read_str
|
48
|
+
puts "\e[0;33mRecieve MWL\e[0m:\n#{wml}" if @debug
|
49
|
+
wml
|
50
|
+
end
|
51
|
+
|
52
|
+
def send_wml node, contents = {}
|
53
|
+
wml = @reader.read_hsh({node => contents}).to_s
|
54
|
+
puts "\e[0;32mSend MWL\e[0m:\n#{wml}" if @debug
|
55
|
+
send_str wml
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def basic_communication
|
61
|
+
handshake
|
62
|
+
raise "Server send node other than 'version'" if read_wml.version.empty?
|
63
|
+
send_wml 'version', :version => @version
|
64
|
+
end
|
65
|
+
|
66
|
+
def handshake
|
67
|
+
@socket.send "\x00" * 4, 0
|
68
|
+
@socket.read 4
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_str
|
72
|
+
msg_size = @socket.read(4).unpack('N').first
|
73
|
+
msg_data = @socket.read(msg_size)
|
74
|
+
Zlib::GzipReader.new(StringIO.new(msg_data)).read
|
75
|
+
end
|
76
|
+
|
77
|
+
def send_str msg
|
78
|
+
gzw = Zlib::GzipWriter.new StringIO.new
|
79
|
+
gzw.write msg.to_s
|
80
|
+
gzm = gzw.close
|
81
|
+
size = [gzm.size].pack('N')
|
82
|
+
@socket.send(size + gzm.string, 0)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/weskit/wml.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class Node
|
3
|
+
FALSE_ATTR_VALS = ["no", "false", "off", "uninitialized"]
|
4
|
+
TRUE_ATTR_VALS = ["yes", "true", "on"]
|
5
|
+
|
6
|
+
attr_reader :name, :parent
|
7
|
+
|
8
|
+
def add element
|
9
|
+
return false unless element.kind_of? Node
|
10
|
+
@nodes << element ; true
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create name = '', parent = nil
|
14
|
+
new name, parent
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize name = '', parent = nil
|
18
|
+
name = normalize name
|
19
|
+
name = (name.empty?) ? 'root' : name
|
20
|
+
raise(NodeError, "Node name can consist only of '#{StringExtensions::VALID_CHARS}'") unless name =~ /^#{StringExtensions::VALID_CHARS}+$/
|
21
|
+
raise(NodeError, "Attempt to set inproper parent node") unless parent.kind_of? Node or parent == nil
|
22
|
+
@attributes, @name, @nodes, @parent = {}, name, [], parent
|
23
|
+
end
|
24
|
+
|
25
|
+
def method_missing method
|
26
|
+
nodes = @nodes.select {|node| node.name == normalize(method)}
|
27
|
+
NodeArray.new.replace nodes
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_ary
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s indent_width = 1
|
34
|
+
indent_width -= 1 if @name == 'root'
|
35
|
+
|
36
|
+
base_indentation = " " * 4
|
37
|
+
contents_indentation = base_indentation * indent_width
|
38
|
+
closing_tag_indentation = base_indentation * (indent_width > 0 ? indent_width - 1 : 0)
|
39
|
+
|
40
|
+
attr_str = @attributes.keys.sort.inject('') {|string, key| string << "#{contents_indentation}#{key}=\"#{@attributes[key]}\"\n"}
|
41
|
+
node_str = @nodes.inject('') {|string, node| string << "#{contents_indentation}#{node.to_s(indent_width + 1)}\n"}
|
42
|
+
|
43
|
+
(@name == 'root') ? "#{attr_str}#{node_str}" : "[#{name}]\n#{attr_str}#{node_str}#{closing_tag_indentation}[/#{name}]"
|
44
|
+
end
|
45
|
+
|
46
|
+
def [] key
|
47
|
+
value = @attributes[normalize key]
|
48
|
+
|
49
|
+
if FALSE_ATTR_VALS.include? value then false
|
50
|
+
elsif TRUE_ATTR_VALS.include? value then true
|
51
|
+
else value
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def []= key, value
|
56
|
+
key, value = normalize(key), value.to_s
|
57
|
+
|
58
|
+
# This one can cause problems as looks there is no escaping strategy on the server side
|
59
|
+
#unless value == '"'
|
60
|
+
# single_quotes, double_quotes = value.scan('"').size, value.scan('""').size
|
61
|
+
# if (single_quotes > 0) and ((double_quotes == 0) or (single_quotes.to_f % double_quotes.to_f > 0))
|
62
|
+
# raise(NodeError, "Found unescaped double quotes inside attribute value")
|
63
|
+
# end
|
64
|
+
# @attributes[key] = value.gsub('"', '""')
|
65
|
+
#end
|
66
|
+
|
67
|
+
@attributes[key] = value
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def normalize name
|
73
|
+
name.to_s.strip
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class NodeArray < Array
|
3
|
+
def [] key
|
4
|
+
unless key.is_a? Integer
|
5
|
+
return first[key] unless empty?
|
6
|
+
nil
|
7
|
+
else
|
8
|
+
super key
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def []= key, value
|
13
|
+
(key.is_a? Integer) ? super(key, value) : (first[key] = value) unless empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing method
|
17
|
+
nodes = inject([]) {|array, element| array + element.send(method) if element.kind_of? Node}
|
18
|
+
NodeArray.new.replace nodes
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_wml
|
22
|
+
inject('') {|string, item| string << "#{item}\n" if item.kind_of? Node}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
class Reader
|
3
|
+
def read_hsh hash, name = '', parent = nil
|
4
|
+
current_node = Node.new name, parent
|
5
|
+
hash.each {|key,val| (val.kind_of? Hash) ? current_node.add(read_hsh(val, key, current_node)) : current_node[key] = val}
|
6
|
+
current_node
|
7
|
+
end
|
8
|
+
|
9
|
+
def read_str string
|
10
|
+
parse string
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_uri uri
|
14
|
+
require 'open-uri'
|
15
|
+
require 'stringio'
|
16
|
+
require 'zlib'
|
17
|
+
|
18
|
+
begin
|
19
|
+
content = open(uri) {|u| u.read}
|
20
|
+
rescue
|
21
|
+
raise ReaderError, "Failed to open specified uri"
|
22
|
+
ensure
|
23
|
+
content = Zlib::GzipReader.new(StringIO.new(content)).read rescue content
|
24
|
+
end
|
25
|
+
|
26
|
+
parse content
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse content
|
32
|
+
current_node, node_stack = Node.new, []
|
33
|
+
multiline_mode, multiline_name, multiline_value = false, '', []
|
34
|
+
|
35
|
+
content.strip.each_line do |line|
|
36
|
+
line = line.chomp.extend StringExtensions
|
37
|
+
|
38
|
+
if multiline_mode
|
39
|
+
if line.end_of_attr?
|
40
|
+
multiline_value << line.rstrip.chomp('"')
|
41
|
+
multiline_value = multiline_value.join("\n")
|
42
|
+
current_node[multiline_name] = multiline_value
|
43
|
+
multiline_mode, multiline_name, multiline_value = false, '', []
|
44
|
+
else
|
45
|
+
multiline_value << line
|
46
|
+
end
|
47
|
+
elsif line.attribute?
|
48
|
+
if line.multiline_attr?
|
49
|
+
multiline_mode, multiline_name = true, line.attribute_name
|
50
|
+
multiline_value << line.attribute_value
|
51
|
+
else
|
52
|
+
current_node[line.attribute_name] = line.attribute_value
|
53
|
+
end
|
54
|
+
elsif line.opening_tag?
|
55
|
+
node_stack.push current_node
|
56
|
+
current_node = Node.create line.tag_name, node_stack.last
|
57
|
+
elsif line.closing_tag?
|
58
|
+
raise(ReaderError, "Opening tag '#{current_node.name}' does not match closing tag '#{line.tag_name}'") if current_node.name != line.tag_name
|
59
|
+
node_stack.last.add current_node
|
60
|
+
current_node = node_stack.pop
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
current_node
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Weskit::WML
|
2
|
+
module StringExtensions
|
3
|
+
VALID_CHARS = '[A-Za-z0-9_]'
|
4
|
+
|
5
|
+
def attribute_name
|
6
|
+
@values[0]
|
7
|
+
end
|
8
|
+
|
9
|
+
def attribute_value
|
10
|
+
@values[1]
|
11
|
+
end
|
12
|
+
|
13
|
+
def multiline_attr?
|
14
|
+
return true if include?('="') and (last_character != '"')
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def end_of_attr?
|
19
|
+
return true if (last_character == '"')
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
def last_character
|
24
|
+
rstrip[-1,1]
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_name
|
28
|
+
@values[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
def opening_tag?
|
32
|
+
wml_check(/^\[(#{VALID_CHARS}+?)\]$/)
|
33
|
+
end
|
34
|
+
|
35
|
+
def closing_tag?
|
36
|
+
wml_check(/^\[\/(#{VALID_CHARS}+?)\]$/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def attribute?
|
40
|
+
# If there is unallowed char in fron of '=' line wont be considereda as an attribute
|
41
|
+
# Multiline attributes need to have double quotes next to equal sign
|
42
|
+
wml_check(/^(#{VALID_CHARS}+?)="?(.+?)"?$/)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def wml_check expression
|
48
|
+
matches = self.strip.match expression
|
49
|
+
|
50
|
+
if matches
|
51
|
+
@values = [matches[1], matches[2]]
|
52
|
+
return true
|
53
|
+
else
|
54
|
+
@values = [nil, nil]
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/weskit.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'weskit/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'weskit'
|
7
|
+
s.version = Weskit::VERSION
|
8
|
+
s.authors = ['f6p']
|
9
|
+
s.email = ['filip.pyda@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/f6p/weskit'
|
11
|
+
s.summary = 'Ruby utilies for BfW'
|
12
|
+
s.description = 'Pls do me a favor and do not look at sources if you dont have to. Thx!'
|
13
|
+
|
14
|
+
# s.rubyforge_project = 'weskit'
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ['lib']
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency 'rspec'
|
23
|
+
# s.add_runtime_dependency 'rest-client'
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weskit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- f6p
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-15 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Pls do me a favor and do not look at sources if you dont have to. Thx!
|
22
|
+
email:
|
23
|
+
- filip.pyda@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- Gemfile
|
33
|
+
- README
|
34
|
+
- Rakefile
|
35
|
+
- lib/weskit.rb
|
36
|
+
- lib/weskit/lobby.rb
|
37
|
+
- lib/weskit/lobby/bot.rb
|
38
|
+
- lib/weskit/lobby/bot_error.rb
|
39
|
+
- lib/weskit/version.rb
|
40
|
+
- lib/weskit/wml.rb
|
41
|
+
- lib/weskit/wml/node.rb
|
42
|
+
- lib/weskit/wml/node_array.rb
|
43
|
+
- lib/weskit/wml/node_error.rb
|
44
|
+
- lib/weskit/wml/reader.rb
|
45
|
+
- lib/weskit/wml/reader_error.rb
|
46
|
+
- lib/weskit/wml/string_extensions.rb
|
47
|
+
- weskit.gemspec
|
48
|
+
homepage: https://github.com/f6p/weskit
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.11
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Ruby utilies for BfW
|
81
|
+
test_files: []
|
82
|
+
|