Added a mass.

Added a mass.

SecsEntity = require('vendor/secs/lib/entity')
Shape = require('lib/geo/shape')
Vector = require('lib/geo/vector')
Direction = require('lib/geo/direction')
Tile = require('lib/geo/tile')

class Entity extends SecsEntity

	new: (@scene, data) =>
		super({
			["is#{@@name}"]: true,
			position: Vector(data),
			Shape(data),
			Tile(data),
			maxForce: 0.02,
			mass: 1
		})

	---
	-- Returns a point found on the entity
	--
	-- @method getPoint
	-- @param {StdDirection} [direction] - Use the point at the specified direction.
	-- @return {Point}
	--
	getPoint: (direction) =>
		if not direction then return @position
		if _.isString(direction) then direction = Direction[direction]

		{ :x, :y } = @position
		{ :width, :height } = @shape

		if direction.x == 0 then x = x + width / 2
		elseif direction.x == 1 then x = x + width
		if direction.y == 0 then y = y + height / 2
		elseif direction.y == 1 then y = y + height

		return Vector(x, y)

	getCenter: () =>
		{ :width, :height } = @shape
		(@position + (@position + Vector(width, height))) / 2

	getData: () =>
		{ :x, :y } = @position
		{ :width, :height } = @shape
		return { :x, :y, :width, :height }

	clone: (scene) => return @@(scene, @getData())

SecsEntity = require('vendor/secs/lib/entity')
Shape = require('lib/geo/shape')
Vector = require('lib/geo/vector')
Direction = require('lib/geo/direction')
Tile = require('lib/geo/tile')

class Entity extends SecsEntity

	new: (@scene, data) =>
		super({
			["is#{@@name}"]: true,
			position: Vector(data),
			Shape(data),
			Tile(data),
			maxForce: 0.02
		})

	---
	-- Returns a point found on the entity
	--
	-- @method getPoint
	-- @param {StdDirection} [direction] - Use the point at the specified direction.
	-- @return {Point}
	--
	getPoint: (direction) =>
		if not direction then return @position
		if _.isString(direction) then direction = Direction[direction]

		{ :x, :y } = @position
		{ :width, :height } = @shape

		if direction.x == 0 then x = x + width / 2
		elseif direction.x == 1 then x = x + width
		if direction.y == 0 then y = y + height / 2
		elseif direction.y == 1 then y = y + height

		return Vector(x, y)

	getCenter: () =>
		{ :width, :height } = @shape
		(@position + (@position + Vector(width, height))) / 2

	getData: () =>
		{ :x, :y } = @position
		{ :width, :height } = @shape
		return { :x, :y, :width, :height }

	clone: (scene) => return @@(scene, @getData())


netutils: support asking for http version

cqueues = require 'cqueues'
dns = require'cqueues.dns'
packet = require'cqueues.dns.packet'
record = require'cqueues.dns.record'
errno = require'cqueues.errno'
socket = require'cqueues.socket'
context = require'openssl.ssl.context'
http_tls = require'http.tls'
pkey = require'openssl.pkey'
auxlib = require"openssl.auxlib"
util = require'util'
{:simplehttp, :bold, :split, :green, :red, :simplehttp, :json, :urlEncode} = require'util'

cache = {}

HasValue = (table, value) ->
  if(type(table) ~= 'table') then return
  for _, v in next, table
    if(v == value) then return true

Good = ->
  "#{green '🔒'} TLS #{green 'ok'}"

Bad = ->
  "#{red '🔓 '} #{red 'Bad'} TLS"

Query = (host, types='AAAA,A', timeout=15) ->
  out = {}

  for type in *split(types, ',')
    answer = dns.query(host, type, 'IN', timeout)

    for rec in answer\grep{section:packet.section.ANSWER}
      out[#out+1] = tostring(rec)

  return out

{
  PRIVMSG: {
    '^%pttls (.*)$': (source, destination, arg) =>
      host = arg\match'^%s*(.-)[:%s].*$' or arg
      port = arg\match'^.*[: ](.*)%s*$' or 443
      port = tonumber(port) or 0
      if port <= 0 or port > 65535
        return reply "Think you're clever?"
      hosts = Query(host)
      if #hosts == 0
        -- assume IP
        hosts = {host}
      found = false
      for ip in *hosts
        sock = socket.connect{host:ip, :port, verify:false, tls_sendname:host}
        ok, sock, err = pcall -> sock\connect 5
        if not ok
          return say "Error connecting to #{ip}:#{port} : #{sock}"
        if ip\match':'
          ip = '[' .. ip .. ']'
        if not sock
          reply "Error #{err} connecting to #{ip}:#{port} : #{errno.strerror err}"
        else
          ctx = http_tls.new_client_context()
          --ctx = context.new('SSL3', false)
          --ctx\setOptions(context.OP_NO_COMPRESSION+contextopenssl_ctx.OP_SINGLE_ECDH_USE)
          --ctx\setEphemeralKey(pkey.new{ type = "EC", curve = "prime256v1" }
          ok, tls, err = pcall -> sock\starttls 5
          if not ok
            return say "TLS negotiation error : #{tls}"
          if tls
            ssl = tls\checktls!
            if sock
              sock\close!
            cipher = ssl\getCipherInfo!
            if http_tls.banned_ciphers[cipher.name]
              return say "Banned cipher: #{cipher.name} when negotiating TLS #{ip}:#{port}"
            cert = ssl\getPeerCertificate!
            before, after = cert\getLifetime!
            cn = tostring cert\getSubject!
            san = cert\getSubjectAlt!
            validhosts = {cn\match'CN=(.*)'}
            for type, sanhost in auxlib.pairs(san)
              table.insert(validhosts, sanhost)
            -- cheat and parse text
            text = cert\text!
            @Log 'debug', text
            expires = text\match('Not After : (.-)%s*Sub')
            verdict = Good!
            reason = ''
            unless HasValue(validhosts, host)
              verdict = Bad!
              reason = "Hostname #{host} not in list #{table.concat validhosts, ', '}"
            if after < os.time!
              verdict = Bad!
              reason ..= " #{red 'Expired'} cert!"

            say "#{verdict} #{reason} #{ip}:#{port}. Expires #{expires} Cipher: #{cipher.bits} bits #{cipher.name}."
          else
            reply "Error #{err} when negotiating TLS #{ip}:#{port} : #{err}"
          found = true
        if found
          break
    '^%pconnect (.*)[:%s]([0-9]+)$': (source, destination, host, port) =>
      port = tonumber port
      if port < 0 or port > 65535
        return reply "Think you're clever?"
      hosts = Query(host)
      if #hosts == 0
        -- assume IP
        hosts = {host}
      for ip in *hosts
        sock = socket.connect(ip, port)
        ok, sock, err = pcall -> sock\connect 5
        if not ok
          return say "Error connecting to #{ip}:#{port} : #{sock}"
        if ok and sock
          sock\close!
        if ip\match':'
          ip = '[' .. ip .. ']'
        if not sock
          reply "Error #{err} connecting to #{ip}:#{port} : #{errno.strerror err}"
        else
          say "Yup, could connect to #{ip}:#{port}"
          break
    '^%pdns (.*)$': (source, destination, arg) =>
      args = split arg, ' '
      argc = #args
      host = args[1]
      types = 'AAAA,A'
      if argc == 2
        types = args[1]
        host = args[2]

      out = Query(host, types)

      if #out == 0
        say "No answers"
      say table.concat(out, ' ')

    '^%pipv6 stats$': (source, destination) =>
      @Send "WHO #{destination}"
    '^%pipv6 stats (.*)$': (source, destination, dest) =>
      @Send "WHO #{dest}"
    '^%phttp (.*)$': (source, destination, arg) =>
      args = split arg, ' '
      argc = #args
      url = args[1]
      field = ''
      if argc == 2
        field = args[1]
        url = args[2]
      unless url\match '^http'
        url = 'http://' .. url
      data, uri, response = simplehttp(url)
      unless data
        reply 'Error: '..tostring(uri)
      unless response
        reply data..uri
      status = response.status_code
      headers = {}
      if argc == 1
        for k, v in pairs response.headers
          headers[#headers+1] = "#{k}:#{v}"
        table.sort(headers)
        headers = table.concat(headers, ', ')
        say "HTTP v#{response.version} #{headers}"
      elseif argc == 2
        field = field\lower!
        value = response.headers[field]
        if field == 'version'
          value = response.version

        say "#{field} : #{value or 'Header not found'}"

  }
  ['352']: {
    (source, destination, input) =>
      --:efnet.port80.se 352 xt #jensaskaret xt 2a02:cc41:100f::1 efnet.port80.se xt H :0 Tor H.
      unless cache[destination]
        cache[destination] = {}

      nick = input.nick
      host = input.host
      --if @channels[destination]
      cache[destination][nick] = host

  }
  ['315']: {
    (source, destination, input) =>
      -- nondeterministic order.. hey ho here we go
      -- TODO use condition?
      cqueues.sleep(2)
      ipv4 = 0
      ipv6 = 0
      cloak = 0
      for nick, host in pairs(cache[destination])
        if host\match ':'
          ipv6 += 1
        elseif host\match '/' -- freenode cloak
          cloak += 1
        elseif host\match '%d+%.%d+%.%d+%.%d+'
          ipv4 += 1
        else
          timeout = 360
          ok, packet, err = pcall(dns.query, host, 'AAAA', 'IN', timeout)
          if not ok
            print 'pack', ok, packet, err
            print(debug.traceback())
          elseif not packet
            print 'no packet for host', host
            ipv4 += 1
          elseif packet\count() >= 2 -- 1 for question and atleast 1 for answer
            ipv6 = ipv6 + 1
          else
            ipv4 = ipv4 + 1
      -- Empty cache
      cache[destination] = nil
      percent = math.floor(100*ipv6/(ipv4+ipv6+cloak))
      out = "IP stats: #{bold percent}% IPv6. #{ipv6} IPv6 clients. #{ipv4} IPv4 clients. "
      if cloak > 0
        out = out .. "#{cloak} cloaked hosts."
      say out
  }
}

cqueues = require 'cqueues'
dns = require'cqueues.dns'
packet = require'cqueues.dns.packet'
record = require'cqueues.dns.record'
errno = require'cqueues.errno'
socket = require'cqueues.socket'
context = require'openssl.ssl.context'
http_tls = require'http.tls'
pkey = require'openssl.pkey'
auxlib = require"openssl.auxlib"
util = require'util'
{:simplehttp, :bold, :split, :green, :red, :simplehttp, :json, :urlEncode} = require'util'

cache = {}

HasValue = (table, value) ->
  if(type(table) ~= 'table') then return
  for _, v in next, table
    if(v == value) then return true

Good = ->
  "#{green '🔒'} TLS #{green 'ok'}"

Bad = ->
  "#{red '🔓 '} #{red 'Bad'} TLS"

Query = (host, types='AAAA,A', timeout=15) ->
  out = {}

  for type in *split(types, ',')
    answer = dns.query(host, type, 'IN', timeout)

    for rec in answer\grep{section:packet.section.ANSWER}
      out[#out+1] = tostring(rec)

  return out

{
  PRIVMSG: {
    '^%pttls (.*)$': (source, destination, arg) =>
      host = arg\match'^%s*(.-)[:%s].*$' or arg
      port = arg\match'^.*[: ](.*)%s*$' or 443
      port = tonumber(port) or 0
      if port <= 0 or port > 65535
        return reply "Think you're clever?"
      hosts = Query(host)
      if #hosts == 0
        -- assume IP
        hosts = {host}
      found = false
      for ip in *hosts
        sock = socket.connect{host:ip, :port, verify:false, tls_sendname:host}
        ok, sock, err = pcall -> sock\connect 5
        if not ok
          return say "Error connecting to #{ip}:#{port} : #{sock}"
        if ip\match':'
          ip = '[' .. ip .. ']'
        if not sock
          reply "Error #{err} connecting to #{ip}:#{port} : #{errno.strerror err}"
        else
          ctx = http_tls.new_client_context()
          --ctx = context.new('SSL3', false)
          --ctx\setOptions(context.OP_NO_COMPRESSION+contextopenssl_ctx.OP_SINGLE_ECDH_USE)
          --ctx\setEphemeralKey(pkey.new{ type = "EC", curve = "prime256v1" }
          ok, tls, err = pcall -> sock\starttls 5
          if not ok
            return say "TLS negotiation error : #{tls}"
          if tls
            ssl = tls\checktls!
            if sock
              sock\close!
            cipher = ssl\getCipherInfo!
            if http_tls.banned_ciphers[cipher.name]
              return say "Banned cipher: #{cipher.name} when negotiating TLS #{ip}:#{port}"
            cert = ssl\getPeerCertificate!
            before, after = cert\getLifetime!
            cn = tostring cert\getSubject!
            san = cert\getSubjectAlt!
            validhosts = {cn\match'CN=(.*)'}
            for type, sanhost in auxlib.pairs(san)
              table.insert(validhosts, sanhost)
            -- cheat and parse text
            text = cert\text!
            @Log 'debug', text
            expires = text\match('Not After : (.-)%s*Sub')
            verdict = Good!
            reason = ''
            unless HasValue(validhosts, host)
              verdict = Bad!
              reason = "Hostname #{host} not in list #{table.concat validhosts, ', '}"
            if after < os.time!
              verdict = Bad!
              reason ..= " #{red 'Expired'} cert!"

            say "#{verdict} #{reason} #{ip}:#{port}. Expires #{expires} Cipher: #{cipher.bits} bits #{cipher.name}."
          else
            reply "Error #{err} when negotiating TLS #{ip}:#{port} : #{err}"
          found = true
        if found
          break
    '^%pconnect (.*)[:%s]([0-9]+)$': (source, destination, host, port) =>
      port = tonumber port
      if port < 0 or port > 65535
        return reply "Think you're clever?"
      hosts = Query(host)
      if #hosts == 0
        -- assume IP
        hosts = {host}
      for ip in *hosts
        sock = socket.connect(ip, port)
        ok, sock, err = pcall -> sock\connect 5
        if not ok
          return say "Error connecting to #{ip}:#{port} : #{sock}"
        if ok and sock
          sock\close!
        if ip\match':'
          ip = '[' .. ip .. ']'
        if not sock
          reply "Error #{err} connecting to #{ip}:#{port} : #{errno.strerror err}"
        else
          say "Yup, could connect to #{ip}:#{port}"
          break
    '^%pdns (.*)$': (source, destination, arg) =>
      args = split arg, ' '
      argc = #args
      host = args[1]
      types = 'AAAA,A'
      if argc == 2
        types = args[1]
        host = args[2]

      out = Query(host, types)

      if #out == 0
        say "No answers"
      say table.concat(out, ' ')

    '^%pipv6 stats$': (source, destination) =>
      @Send "WHO #{destination}"
    '^%pipv6 stats (.*)$': (source, destination, dest) =>
      @Send "WHO #{dest}"
    '^%phttp (.*)$': (source, destination, arg) =>
      args = split arg, ' '
      argc = #args
      url = args[1]
      field = ''
      if argc == 2
        field = args[1]
        url = args[2]
      unless url\match '^http'
        url = 'http://' .. url
      data, uri, response = simplehttp(url)
      unless data
        reply 'Error: '..tostring(uri)
      unless response
        reply data..uri
      status = response.status_code
      headers = {}
      if argc == 1
        for k, v in pairs response.headers
          headers[#headers+1] = "#{k}:#{v}"
        table.sort(headers)
        headers = table.concat(headers, ', ')
        say "HTTP v#{response.version} #{headers}"
      elseif argc == 2
        field = field\lower!
        say "#{field} : #{response.headers[field] or 'Header not found'}"

  }
  ['352']: {
    (source, destination, input) =>
      --:efnet.port80.se 352 xt #jensaskaret xt 2a02:cc41:100f::1 efnet.port80.se xt H :0 Tor H.
      unless cache[destination]
        cache[destination] = {}

      nick = input.nick
      host = input.host
      --if @channels[destination]
      cache[destination][nick] = host

  }
  ['315']: {
    (source, destination, input) =>
      -- nondeterministic order.. hey ho here we go
      -- TODO use condition?
      cqueues.sleep(2)
      ipv4 = 0
      ipv6 = 0
      cloak = 0
      for nick, host in pairs(cache[destination])
        if host\match ':'
          ipv6 += 1
        elseif host\match '/' -- freenode cloak
          cloak += 1
        elseif host\match '%d+%.%d+%.%d+%.%d+'
          ipv4 += 1
        else
          timeout = 360
          ok, packet, err = pcall(dns.query, host, 'AAAA', 'IN', timeout)
          if not ok
            print 'pack', ok, packet, err
            print(debug.traceback())
          elseif not packet
            print 'no packet for host', host
            ipv4 += 1
          elseif packet\count() >= 2 -- 1 for question and atleast 1 for answer
            ipv6 = ipv6 + 1
          else
            ipv4 = ipv4 + 1
      -- Empty cache
      cache[destination] = nil
      percent = math.floor(100*ipv6/(ipv4+ipv6+cloak))
      out = "IP stats: #{bold percent}% IPv6. #{ipv6} IPv6 clients. #{ipv4} IPv4 clients. "
      if cloak > 0
        out = out .. "#{cloak} cloaked hosts."
      say out
  }
}


Minor function move in Graphic

Minor function move in Graphic

require "LovePunk.LP"
require "LovePunk.geometry.Point"

export ^

class Graphic
	new: =>
		-- If the graphic should update.
		@active = false
		-- If the graphic should render.
		@visible = true
		-- X offset.
		@x = 0.0
		-- Y offset.
		@y = 0.0
		-- X scrollfactor, effects how much the camera offsets the drawn graphic. Can be used for parallax effect, eg. Set to 0 to follow the camera, 0.5 to move at half-speed of the camera, or 1 (default) to stay still.
		@scrollX = 1.0
		-- Y scrollfactor, effects how much the camera offsets the drawn graphic. Can be used for parallax effect, eg. Set to 0 to follow the camera, 0.5 to move at half-speed of the camera, or 1 (default) to stay still.
		@scrollY = 1.0
		-- If the graphic should render at its position relative to its parent Entity's position.
		@relative = true
		-- The layer to use for rendering, should only be set by the Entity
		@__layer = LP.BASELAYER!
		@__scroll = true
		@__point = Point!
		@__entity = nil

	-- Updates the graphic.
	update: =>

	-- Removes the graphic from the scene
	destroy: =>

	-- Renders the graphic to the screen buffer.
	-- @param  target     The buffer to draw to.
	-- @param  point      The position to draw the graphic.
	-- @param  camera     The camera offset.
	render: (target, point, camera) =>

	setEntity: (entity) =>
		@__entity = entity
		layer = if entity != nil
				entity.layer
			else
				LP.BASELAYER!

	-- Pause updating this graphic.
	pause: => @active = false

	-- Resume updating this graphic.
	resume: => @active = true

	-- The layer to use for rendering, should only be set by the Entity
	layer: => @__layer

require "LovePunk.LP"
require "LovePunk.geometry.Point"

export ^

class Graphic
	new: =>
		-- If the graphic should update.
		@active = false
		-- If the graphic should render.
		@visible = true
		-- X offset.
		@x = 0.0
		-- Y offset.
		@y = 0.0
		-- X scrollfactor, effects how much the camera offsets the drawn graphic. Can be used for parallax effect, eg. Set to 0 to follow the camera, 0.5 to move at half-speed of the camera, or 1 (default) to stay still.
		@scrollX = 1.0
		-- Y scrollfactor, effects how much the camera offsets the drawn graphic. Can be used for parallax effect, eg. Set to 0 to follow the camera, 0.5 to move at half-speed of the camera, or 1 (default) to stay still.
		@scrollY = 1.0
		-- If the graphic should render at its position relative to its parent Entity's position.
		@relative = true
		-- The layer to use for rendering, should only be set by the Entity
		@__layer = LP.BASELAYER!
		@__scroll = true
		@__point = Point!
		@__entity = nil

	layer: =>
		@__layer

	-- Updates the graphic.
	update: =>

	-- Removes the graphic from the scene
	destroy: =>

	-- Renders the graphic to the screen buffer.
	-- @param  target     The buffer to draw to.
	-- @param  point      The position to draw the graphic.
	-- @param  camera     The camera offset.
	render: (target, point, camera) =>

	setEntity: (entity) =>
		@__entity = entity
		layer = if entity != nil
				entity.layer
			else
				LP.BASELAYER!

	-- Pause updating this graphic.
	pause: => @active = false

	-- Resume updating this graphic.
	resume: => @active = true