Update socket.moon

Update socket.moon

ffi = require 'ffi'

ffi.cdef [[
    typedef long SOCKET;
    int cls_socket(int af, int type, int proto);
    int cls_connect(SOCKET s, const char * host, int port, int family);
    int cls_bind(SOCKET s, const char * host, int port);
    int cls_listen(SOCKET s, int backlog);
    int cls_select(SOCKET s, int tmout);
    SOCKET cls_accept(SOCKET s);
    int cls_send(SOCKET s, const char * message);
    int cls_avaliable(SOCKET s);
    int cls_receive(SOCKET s, char * buffer, int len);
    int cls_close(SOCKET s);
]]

clsocket = ffi.load 'clsocket.dll'

class Socket
    new: (af, ty, family) =>
        @descriptor = clsocket.cls_socket(af, ty, family)
        if @descriptor == 1 then error 'error socket'

    connect: (host, port, family = nil) =>
        conn = clsocket.cls_connect @descriptor, host, port, family or 2
        if conn == 1 then error 'error connect'

    bind: (host, port) =>
        bind = clsocket.cls_bind @descriptor, host, port
        if bind == 1 then error 'error bind'

    listen: (backlog) =>
        listen = clsocket.cls_listen @descriptor, backlog
        if listen == 1 then error 'error listen'

    accept: =>
        sck = clsocket.cls_accept!
        if sck == 1 then error 'error accept'
        return sck

    send: (message) =>
        send = clsocket.cls_send @descriptor, message
        if send == 1 then error 'error send'

    receive: (len) =>
        buff = ffi.new 'char[?]', len
        recv = clsocket.cls_receive @descriptor, buff, len
        if recv == 1 then error 'error receive'
        return ffi.string(buff)

    avaliable: =>
        av = clsocket.cls_avaliable @descriptor
        if av == -1 then error 'error avaliable'
        return av

    close: =>
        clsocket.cls_close @descriptor

    select: (sck, timeout) ->
        if type sck ~= 'number' then sck = sck.socket
        selc = clsocket.cls_select sck, timeout
        if selc == 1 then error 'error select'
        return selc == 0

{ :Socket }

ffi = require 'ffi'

ffi.cdef [[
    typedef long SOCKET;
    int cls_socket(int af, int type, int proto);
    int cls_connect(SOCKET s, const char * host, int port, int family);
    int cls_bind(SOCKET s, const char * host, int port);
    int cls_listen(SOCKET s, int backlog);
    int cls_select(SOCKET s, int tmout);
    SOCKET cls_accept(SOCKET s);
    int cls_send(SOCKET s, const char * message);
    int cls_avaliable(SOCKET s);
    int cls_receive(SOCKET s, char * buffer, int len);
    int cls_close(SOCKET s);
]]

clsocket = ffi.load 'clsocket.dll'

class Socket
    new: (af, ty, family) =>
        @descriptor = clsocket.cls_socket(af, ty, family)
        if @descriptor == 1 then error 'error socket'

    connect: (host, port, family = nil) =>
        conn = clsocket.cls_connect @descriptor, host, port, family or 2
        if conn == 1 then error 'error connect'

    bind: (host, port) =>
        bind = clsocket.cls_bind @descriptor, host, port
        if bind == 1 then error 'error bind'

    listen: (backlog) =>
        listen = clsocket.cls_listen @descriptor, backlog
        if listen == 1 then error 'error listen'

    accept: =>
        sck = clsocket.cls_accept!
        if sck == 1 then error 'error accept'
        return sck

    send: (message) =>
        send = clsocket.cls_send @descriptor, message
        if send == 1 then error 'error send'

    receive: (len) =>
        buff = ffi.new 'char[?]', len
        recv = clsocket.cls_receive @descriptor, buff, len
        if recv == 1 then error 'error receive'
        return ffi.string(buff)

    avaliable: =>
        av = clsocket.cls_avaliable @descriptor
        if av == -1 then error 'error avaliable'
        return av

    close: =>
        clsocket.cls_close @descriptor

    select: (sck, timeout) ->
        if type sck ~= 'number' then sck = sck.socket
        selc = clsocket.cls_select sck, timeout
        if selc == 1 then error 'error select'
        return selc == 0

{ :Socket, :AsyncTcpServer }


acl logic fix

acl logic fix

import Model, enum from require "lapis.db.model"
import create_table, types, add_column from require "lapis.db.schema"

Users = require "lazuli.modules.user_management.models.users"
ACL_Entries = require "models.acl_entries"

class ACLs extends Model

  -- user is one of:
  --  * "lazuli.modules.user_management.models.users" instance
  --  * user id (number) to be found by that model
  --  * nil if no user is logged in/applicable
  matchUser: do
    _logic= (user,return_default=true)->
      entries=ACL_Entries\select "where acl_id = ? order by position asc nulls first, id", @id
      for entry in *entries
        ret, err=entry\matchUser user
        return nil, err if ret == nil and err
        return ret if type(ret)=="boolean"
      if return_default
        return @default_policy
    (user,return_default=true)=>
      if user
        if type(user)=="number"
          user=Users\find user
        return nil, "user not found" unless user
      cname=(user and tostring(user.id) or "[nil]").."-"..@id
      cached=ngx.shared.acl_cache\get cname
      return cached if cached ~= nil
      ret,err=_logic user, return_default
      if type(ret)=="boolean"
        ngx.shared.acl_cache\set cname, ret, 10
      return ret, err

  @relations: {
    {"user", belongs_to: "Users"}
  }

  @get_relation_model: (name)=> switch name
    when "Users"
      require "lazuli.modules.user_management.models.users"
    when "ACLs"
      require "models.acls"

  @migrations: {
    ->
      create_table "acls", {
        {"id",             types.serial}
        {"user_id",        types.integer}
        {"name",           types.varchar}
        {"default_policy", types.boolean}
        "PRIMARY KEY (id)"
      }
  }

import Model, enum from require "lapis.db.model"
import create_table, types, add_column from require "lapis.db.schema"

Users = require "lazuli.modules.user_management.models.users"
ACL_Entries = require "models.acl_entries"

class ACLs extends Model

  -- user is one of:
  --  * "lazuli.modules.user_management.models.users" instance
  --  * user id (number) to be found by that model
  --  * nil if no user is logged in/applicable
  matchUser: do
    _logic= (user,return_default=true)=>
      entries=ACL_Entries\select "where acl_id = ? order by position asc nulls first, id", @id
      for entry in *entries
        ret, err=entry\matchUser user
        return nil, err if ret == nil and err
        return ret if type(ret)=="boolean"
      if return_default
        return @default_policy
    (user,return_default=true)=>
      if user
        if type(user)=="number"
          user=Users\find user
        return nil, "user not found" unless user
      cname=(user and tostring(user.id) or "[nil]").."-"..@id
      cached=ngx.shared.acl_cache\get cname
      return cached if cached ~= nil
      ret,err=_logic user, return_default
      if type(ret)=="boolean"
        ngx.shared.acl_cache\set cname, ret, 10
      return ret, err

  @relations: {
    {"user", belongs_to: "Users"}
  }

  @get_relation_model: (name)=> switch name
    when "Users"
      require "lazuli.modules.user_management.models.users"
    when "ACLs"
      require "models.acls"

  @migrations: {
    ->
      create_table "acls", {
        {"id",             types.serial}
        {"user_id",        types.integer}
        {"name",           types.varchar}
        {"default_policy", types.boolean}
        "PRIMARY KEY (id)"
      }
  }


utf8: prevent zalgoDOS

utf8: prevent zalgoDOS

util = require'util'
html2unicode = require'html'

an = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.?!"'`()[]{}<>&_]]
charmaps = {
ci: 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl: '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'
ud: [[∀BƆDƎℲפHIſK˥WNOԀQRS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz0ƖᄅƐㄣϛ9ㄥ86'˙¿¡,,,)(][}{><⅋‾]]
nc: [[🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩⓿]]
sq: [[🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉0123456789,⊡]]
ns: [[🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉]]
ds: [[𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡]]
bo: [[𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗]]
bi: [[𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛0123456789]]
bs: [[𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃]]
pt: [[⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵0⑴⑵⑶⑷⑸⑹⑺⑻⑼]]
tl: [[ค๒ς๔єŦﻮђเןкl๓ภ๏קợгรtยשฬץאzค๒ς๔єŦﻮђเןкl๓ภ๏קợгรtยשฬץאz0123456789,.؟!"'`()[]{}«»&_]]
}
codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

-- Construct a table which can be used for lookup replacement later
-- Iterate over normal ascii and find counterparts in the weirdo strings as substrings
maps = {}
for charmap, chars in pairs charmaps
  i = 1
  maps[charmap] = {}
  for uchar in util.utf8.chars(chars)
    maps[charmap][an\sub(i,i)] = uchar
    i = i +1

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  -- Limit text to prevent DoS
  text = text\sub(1,512)
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    say wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    say remap(maps.bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    say remap(maps.ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    say zalgo(arg, 7)
  '^%pupsidedown (.+)$': (source, destination, arg) =>
    say remap(maps.ud, util.utf8.reverse(arg))
  '^%pflip (.+)$': (source, destination, arg) =>
    say remap(maps.ud, arg)
  '^%pthrow (.+)$': (source, destination, arg) =>
    say "（╯°□°）╯︵ #{remap maps.ud, util.utf8.reverse(arg)}"
  '^%pparanthesized (.+)$': (source, destination, arg) =>
    say remap(maps.pt, arg)
  '^%pnegcircle (.+)$': (source, destination, arg) =>
    say remap(maps['nc'], arg)
  '^%psquare (.+)$': (source, destination, arg) =>
    say remap(maps.sq, arg)
  '^%pnegsquare (.+)$': (source, destination, arg) =>
    say remap(maps.ns, arg)
  '^%pdoublestruck (.+)$': (source, destination, arg) =>
    say remap(maps.ds, arg)
  '^%pubold (.+)$': (source, destination, arg) =>
    say remap(maps.bo, arg)
  '^%pbolditalic (.+)$': (source, destination, arg) =>
    say remap(maps.bi, arg)
  '^%pboldscript (.+)$': (source, destination, arg) =>
    say remap(maps.bs, arg)
  '^%pthai (.+)$': (source, destination, arg) =>
    say remap(maps.tl, arg)
  '^%putfuk (.+)$': (source, destination, arg) =>
    keys = [x for x,_ in pairs(maps)]
    say table.concat([remap(maps[keys[math.random(#keys)]], letter) for letter in codepoints(arg)])

util = require'util'
html2unicode = require'html'

an = [[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.?!"'`()[]{}<>&_]]
charmaps = {
ci: 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪①②③④⑤⑥⑦⑧⑨'
bl: '𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷'
ud: [[∀BƆDƎℲפHIſK˥WNOԀQRS┴∩ΛMX⅄Zɐqɔpǝɟƃɥᴉɾʞlɯuodbɹsʇnʌʍxʎz0ƖᄅƐㄣϛ9ㄥ86'˙¿¡,,,)(][}{><⅋‾]]
nc: [[🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩⓿]]
sq: [[🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉0123456789,⊡]]
ns: [[🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉]]
ds: [[𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡]]
bo: [[𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗]]
bi: [[𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛0123456789]]
bs: [[𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃]]
pt: [[⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵0⑴⑵⑶⑷⑸⑹⑺⑻⑼]]
tl: [[ค๒ς๔єŦﻮђเןкl๓ภ๏קợгรtยשฬץאzค๒ς๔єŦﻮђเןкl๓ภ๏קợгรtยשฬץאz0123456789,.؟!"'`()[]{}«»&_]]
}
codepoints = (str) ->
  str\gmatch("[%z\1-\127\194-\244][\128-\191]*")

-- Construct a table which can be used for lookup replacement later
-- Iterate over normal ascii and find counterparts in the weirdo strings as substrings
maps = {}
for charmap, chars in pairs charmaps
  i = 1
  maps[charmap] = {}
  for uchar in util.utf8.chars(chars)
    maps[charmap][an\sub(i,i)] = uchar
    i = i +1

unichr = (n) ->
  html2unicode('&#x%x;'\format(n))

wireplace = (offset, arg) ->
    s = arg or ''
    t = {}
    for i = 1, #s
      bc = string.byte(s, i, i)
      -- Replace space width ideographic space for fullwidth offset
      if bc == 32 and offset == 0xFEE0
        t[#t + 1] = '\227\128\128'
      elseif bc == 32
        t[#t + 1] = ' '
      elseif bc < 0x80 then
        t[#t + 1] = html2unicode("&#" .. (offset + bc) .. ";")
      else
        t[#t + 1] = s\sub(i, i)

    table.concat(t, "")

remap = (map, s) ->
  table.concat [map[s\sub(i,i)] or s\sub(i,i) for i=1, #s], ''

zalgo = (text, intensity=50) ->
  zalgo_chars = {}
  for i=0x0300, 0x036f
    zalgo_chars[i-0x2ff] = unichr(i)

  zalgo_chars[#zalgo_chars + 1] = unichr(0x0488)
  zalgo_chars[#zalgo_chars + 0] = unichr(0x0489)

  zalgoized = {}
  for letter in codepoints(text)
    zalgoized[#zalgoized + 1] = letter
    zalgo_num = math.random(1, intensity)
    for i=1, zalgo_num
      zalgoized[#zalgoized + 1] = zalgo_chars[math.random(1, #zalgo_chars)]
  table.concat(zalgoized)

PRIVMSG:
  '^%pwide (.+)$': (source, destination, arg) =>
    say wireplace(0xFEE0, arg)
  '^%pblackletter (.+)$': (source, destination, arg) =>
    say remap(maps.bl, arg)
  '^%pcircled (.+)$': (source, destination, arg) =>
    say remap(maps.ci, arg)
  '^%pzalgo (.+)$': (source, destination, arg) =>
    say zalgo(arg, 7)
  '^%pupsidedown (.+)$': (source, destination, arg) =>
    say remap(maps.ud, util.utf8.reverse(arg))
  '^%pflip (.+)$': (source, destination, arg) =>
    say remap(maps.ud, arg)
  '^%pthrow (.+)$': (source, destination, arg) =>
    say "（╯°□°）╯︵ #{remap maps.ud, util.utf8.reverse(arg)}"
  '^%pparanthesized (.+)$': (source, destination, arg) =>
    say remap(maps.pt, arg)
  '^%pnegcircle (.+)$': (source, destination, arg) =>
    say remap(maps['nc'], arg)
  '^%psquare (.+)$': (source, destination, arg) =>
    say remap(maps.sq, arg)
  '^%pnegsquare (.+)$': (source, destination, arg) =>
    say remap(maps.ns, arg)
  '^%pdoublestruck (.+)$': (source, destination, arg) =>
    say remap(maps.ds, arg)
  '^%pubold (.+)$': (source, destination, arg) =>
    say remap(maps.bo, arg)
  '^%pbolditalic (.+)$': (source, destination, arg) =>
    say remap(maps.bi, arg)
  '^%pboldscript (.+)$': (source, destination, arg) =>
    say remap(maps.bs, arg)
  '^%pthai (.+)$': (source, destination, arg) =>
    say remap(maps.tl, arg)
  '^%putfuk (.+)$': (source, destination, arg) =>
    keys = [x for x,_ in pairs(maps)]
    say table.concat([remap(maps[keys[math.random(#keys)]], letter) for letter in codepoints(arg)])


fixed yubikey

fixed yubikey

import Model from require "lapis.db.model"
import Users from require "lazuli.modules.user_management.models.users"

class YubiCloud extends Model
  @table_name: => "lazuli_modules_user_management_yubicloud"
  @relations: {
    {"user", belongs_to: Users}
  }

import Model from require "lapis.db.model"
import Users from require "lazuli.modules.user_management.models.users"

class YubiCloud extends Model
  @table_name: => "lazuli_modules_user_management_yubicloud"
  @relations: {
    {"user", belongs_to: "Users"}
  }


fixed yubikey

fixed yubikey

import Model from require "lapis.db.model"
import Users from require "lazuli.modules.user_management.models.users"

class YubiCloud extends Model
  @table_name: => "lazuli_modules_user_management_yubicloud"
  @relations: {
    {"user", belongs_to: "lazuli.modules.user_management.models.users"}
  }

import Model from require "lapis.db.model"
import Users from require "lazuli.modules.user_management.models.users"

class YubiCloud extends Model
  @table_name: => "lazuli_modules_user_management_yubicloud"
  @relations: {
    {"user", belongs_to: Users}
  }


add urls in module description pages

add urls in module description pages

class Module extends require "views.base"
  content: =>
    h2 class: "list-header", @module\get_name()
    @render_errors()
    div class: "cell", ->
      @render_endorse_button(@module)
      p class: "module-description", @module\get_description()
      url = @module\get_url()
      p a href: url, url
      @render_endorsers_and_labels(@module)
      if @current_user and (@current_user\get_trust_level() > 1)
        form method: "POST", action: @url_for("main.module", id: @module.id), ->
          input type: "text", name: "label"
          input type: "submit", value: "add label"
          input type: "hidden", name: "action", value: "label"

class Module extends require "views.base"
  content: =>
    h2 class: "list-header", @module\get_name()
    @render_errors()
    div class: "cell", ->
      @render_endorse_button(@module)
      p class: "module-description", @module\get_description()
      @render_endorsers_and_labels(@module)
      if @current_user and (@current_user\get_trust_level() > 1)
        form method: "POST", action: @url_for("main.module", id: @module.id), ->
          input type: "text", name: "label"
          input type: "submit", value: "add label"
          input type: "hidden", name: "action", value: "label"


fuck bitmasks, we're going for boolean columns.

import Model, enum from require "lapis.db.model"
import create_table, types, add_column, drop_column from require "lapis.db.schema"
import band, bor from require "bit"
Users = require "lazuli.modules.user_management.models.users"
config = (require "lapis.config").get!

class Timelines extends Model
  @relations: {
    {"user",               belongs_to: "Users"}
    {"acl",                belongs_to: "ACLs"}
  }

  @get_relation_model: (name)=> switch name
    when "Users"
      require "lazuli.modules.user_management.models.users"
    when "ACLs"
      require "models.acls"

  @migrations: {
    ->
      create_table "timelines", {
        {"id", types.serial}
        {"user_id", types.integer}
        {"acl_id", types.integer}
        --{"flag_include_posts_text", types.boolean}
        --{"flag_include_posts_image", types.boolean}
        --{"flag_include_posts_video", types.boolean}
        --{"flag_include_comments", types.boolean}
        --{"flag_include_likes", types.boolean}
        --{"flag_include_profiles", types.boolean}
        "PRIMARY KEY (id)"
      }

  }

import Model, enum from require "lapis.db.model"
import create_table, types, add_column, drop_column from require "lapis.db.schema"
import band, bor from require "bit"
Users = require "lazuli.modules.user_management.models.users"
config = (require "lapis.config").get!

class Timelines extends Model
  @relations: {
    {"user",               belongs_to: "Users"}
    {"acl",                belongs_to: "ACLs"}
    {"flags",              fetch: =>
      flags={}
      for k,v in pairs @@flags_values
        if type(k)=="number" and 0~=band(@flags_mask, 2^k)
          flags[v]=true
      flags
    }
  }
  @constraints: {
    flags: (tbl,_,obj) =>
      val=0
      for k,v in pairs @flags_values
        if type(k)=="number" and tbl[v]
          val=bor(val,2^k)
      obj.flags_mask=val
      obj.flags=nil
      false
  }

  @flags_values: {
    "markdown_posts"
    "image_posts"
    "video_posts"
    "profile_updates"
    "comments"
  }

  @get_relation_model: (name)=> switch name
    when "Users"
      require "lazuli.modules.user_management.models.users"
    when "ACLs"
      require "models.acls"

  @migrations: {
    ->
      create_table "timelines", {
        {"id", types.serial}
        {"user_id", types.integer}
        {"acl_id", types.integer}
        {"flags_mask", types.integer}
        "PRIMARY KEY (id)"
      }

  }


Adjuct jump height only on server

Adjuct jump height only on server

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

for ply in *player.GetAll()
    ply.__PPM2_PonyData\Remove() if ply.__PPM2_PonyData

class NetworkedPonyData extends PPM2.NetworkedObject
    @NW_ClientsideCreation = true
    @RenderTasks = {}

    @Setup()
    @NetworkVar('Entity',           net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), ((newValue) => IsValid(@GetOwner()) and StrongEntity(@GetOwner()\EntIndex()) or newValue))
    @NetworkVar('UpperManeModel',   net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('LowerManeModel',   net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('TailModel',        net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('SocksModel',       net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)

    @NetworkVar('Race',             (-> math.Clamp(net.ReadUInt(4), 0, 3)), ((arg = PPM2.RACE_EARTH) -> net.WriteUInt(arg, 4)), PPM2.RACE_EARTH)
    @NetworkVar('Gender',           (-> math.Clamp(net.ReadUInt(4), 0, 1)), ((arg = PPM2.GENDER_FEMALE) -> net.WriteUInt(arg, 4)), PPM2.GENDER_FEMALE)
    @NetworkVar('Weight',           (-> math.Clamp(net.ReadFloat(), PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)), net.WriteFloat, 1)
    @NetworkVar('PonySize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_SCALE, PPM2.MAX_SCALE)),   net.WriteFloat, 1)

    -- Reserved - they can be accessed/used/changed, but they do not do anything
    @NetworkVar('Age',              (-> math.Clamp(net.ReadUInt(4), 0, 2)), ((arg = PPM2.AGE_ADULT) -> net.WriteUInt(arg, 4)), PPM2.AGE_ADULT)

    @NetworkVar('EyelashType',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)),           ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('TailType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS, PPM2.MAX_TAILS)),                   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLower',    (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('TailTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS_NEW, PPM2.MAX_TAILS_NEW)),               ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES_NEW, PPM2.MAX_UPPER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLowerNew', (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES_NEW, PPM2.MAX_LOWER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('BodyColor',        net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))

    @NetworkVar('SeparateEyes',     net.ReadBool, net.WriteBool,              false)
    for publicName in *{'', 'Left', 'Right'}
        @NetworkVar("EyeType#{publicName}",          (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYE_TYPE, PPM2.MAX_EYE_TYPE)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("EyeBackground#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeHole#{publicName}",          net.ReadColor, net.WriteColor, 	    Color(0,   0,   0  ))
        @NetworkVar("EyeIrisTop#{publicName}",       net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisBottom#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisLine1#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeIrisLine2#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeReflection#{publicName}",    net.ReadColor, net.WriteColor,    Color(255, 255, 255, 127))
        @NetworkVar("EyeEffect#{publicName}",        net.ReadColor, net.WriteColor,         Color(255, 255, 255))
        @NetworkVar("EyeLines#{publicName}",         net.ReadBool, net.WriteBool,                           true)
        @NetworkVar("DerpEyes#{publicName}",         net.ReadBool, net.WriteBool,                          false)
        @NetworkVar("DerpEyesStrength#{publicName}", (-> math.Clamp(net.ReadFloat(), PPM2.MIN_DERP_STRENGTH, PPM2.MAX_DERP_STRENGTH)), net.WriteFloat, 1)
        @NetworkVar("HoleWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat,  1)
        @NetworkVar("HoleHeight#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat,  1)
        @NetworkVar("HoleSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE, PPM2.MAX_HOLE)),             net.WriteFloat, .8)
        @NetworkVar("HoleShiftX#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)), net.WriteFloat, 0)
        @NetworkVar("HoleShiftY#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)), net.WriteFloat, 0)
        @NetworkVar("IrisSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, .8)
        @NetworkVar("IrisWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, 1)
        @NetworkVar("IrisHeight#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, 1)
        @NetworkVar("EyeRotation#{publicName}",      (-> math.Clamp(net.ReadInt(12), PPM2.MIN_EYE_ROTATION, PPM2.MAX_EYE_ROTATION)), ((arg = 0) -> net.WriteInt(arg, 12)), 1)
        @NetworkVar("EyeURL#{publicName}",           net.ReadString,                                                            net.WriteString, '')

    @NetworkVar('SeparateMane',     net.ReadBool, net.WriteBool,              false)
    for i = 1, 6
        @NetworkVar("ManeColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("TailColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("LowerManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("LowerManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))

        @NetworkVar("UpperManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("UpperManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))

    @NetworkVar('CMark',            net.ReadBool, net.WriteBool,              true)
    @NetworkVar('CMarkSize',        (-> math.Clamp(net.ReadFloat(), 0, 1)), net.WriteFloat, 1)
    @NetworkVar('CMarkColor',       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
    @NetworkVar('CMarkURL',         net.ReadString, net.WriteString,            '')
    @NetworkVar('CMarkType',        (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_CMARK, PPM2.MAX_CMARK)),           ((arg = 4) -> net.WriteUInt(arg, 8)), 4)
    @NetworkVar('TailSize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)),   net.WriteFloat, 1)

    @NetworkVar('Bodysuit',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SUIT, PPM2.MAX_SUIT)),             ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('Socks',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('NoFlex',           net.ReadBool, net.WriteBool,              false)

    @NetworkVar('SocksAsModel',     net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksTexture',     (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SOCKS, PPM2.MAX_SOCKS)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('SocksColor',       net.ReadColor, net.WriteColor,            Color(255, 255, 255))
    @NetworkVar('SocksTextureURL',  net.ReadString, net.WriteString,          '')

    for i = 1, 6
        @NetworkVar('SocksDetailColor' .. i, net.ReadColor, net.WriteColor, Color(255, 255, 255))

    @NetworkVar('BatPonyEars',      net.ReadBool, net.WriteBool,              false)
    @NetworkVar('Fangs',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('ClawTeeth',        net.ReadBool, net.WriteBool,              false)

    @NetworkVar('TeethColor',       net.ReadColor, net.WriteColor,    Color(255, 255, 255))
    @NetworkVar('MouthColor',       net.ReadColor, net.WriteColor,    Color(219, 65, 155))
    @NetworkVar('TongueColor',      net.ReadColor, net.WriteColor,    Color(235, 131, 59))

    for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
        @NetworkVar("DisableFlex#{flex}", net.ReadBool, net.WriteBool, false) if active

    for i = 1, PPM2.MAX_BODY_DETAILS
        @NetworkVar("BodyDetail#{i}",       (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_DETAIL, PPM2.MAX_DETAIL)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("BodyDetailColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BodyDetailURL#{i}",    net.ReadString, net.WriteString, '')
        @NetworkVar("BodyDetailURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    for i = 1, 3
        @NetworkVar("HornURL#{i}",       net.ReadString, net.WriteString, '')
        @NetworkVar("WingsURL#{i}",      net.ReadString, net.WriteString, '')
        @NetworkVar("HornURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("WingsURLColor#{i}", net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    @NetworkVar('Fly',                  net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('DisableTask',          net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('UseFlexLerp',          net.ReadBool,   net.WriteBool,                  true)
    @NetworkVar('FlexLerpMultiplier',   net.ReadFloat,  net.WriteFloat,                    1)
    @NetworkVar('NewMuzzle',            net.ReadBool,   net.WriteBool,                  true)
    @NetworkVar('SeparateWings',        net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('SeparateHorn',         net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('WingsColor',           net.ReadColor,  net.WriteColor,  Color(255, 255, 255))
    @NetworkVar('HornColor',            net.ReadColor,  net.WriteColor,  Color(255, 255, 255))

    @NetworkVar('WingsType',            (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_WINGS, PPM2.MAX_WINGS)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('MaleBuff',             (-> math.Clamp(net.ReadFloat(), PPM2.MIN_MALE_BUFF, PPM2.MAX_MALE_BUFF)), net.WriteFloat, PPM2.DEFAULT_MALE_BUFF)

    @NetworkVar('BatWingColor',         net.ReadColor, net.WriteColor,    Color(255, 255, 255))
    @NetworkVar('BatWingSkinColor',     net.ReadColor, net.WriteColor,    Color(255, 255, 255))

    for i = 1, 3
        @NetworkVar("BatWingURL#{i}",           net.ReadString, net.WriteString, '')
        @NetworkVar("BatWingSkinURL#{i}",       net.ReadString, net.WriteString, '')
        @NetworkVar("BatWingURLColor#{i}",      net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BatWingSkinURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    Clone: (target = @ent) =>
        copy = @@(nil, target)
        @ApplyDataToObject(copy)
        return copy

    new: (netID, ent) =>
        @recomputeTextures = true
        @isValid = true
        if ent
            @modelCached = ent\GetModel()
            @SetEntity(ent)
            @SetupEntity(ent)
        super(netID)
    IsValid: => @isValid
    GetModel: => @modelCached
    EntIndex: => @entID
    SetupEntity: (ent) =>
        if ent.__PPM2_PonyData
            return if ent.__PPM2_PonyData\GetOwner() ~= @GetOwner()
            ent.__PPM2_PonyData\Remove() if ent.__PPM2_PonyData.Remove and ent.__PPM2_PonyData ~= @
        ent.__PPM2_PonyData = @
        @ent = ent
        return unless IsValid(ent)
        @modelCached = ent\GetModel()
        @ent = ent
        @flightController = PPM2.PonyflyController(@)
        @entID = ent\EntIndex()
        @ModelChanges(@modelCached, @modelCached)
        @Reset()
        if CLIENT
            timer.Simple 0, ->
                @GetRenderController()\CompileTextures() if @GetRenderController()
        PPM2.DebugPrint('Ponydata ', @, ' was updated to use for ', @ent)
        @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]
    ModelChanges: (old = @ent\GetModel(), new = old) =>
        @modelCached = new
        @SetFly(false) if SERVER
        timer.Simple 0.5, ->
            return unless IsValid(@ent)
            @GetBodygroupController()\ApplyBodygroups(CLIENT) if @GetBodygroupController()
            if CLIENT
                @GetRenderController()
                @GetWeightController()
            @Reset()
    GenericDataChange: (state) =>
        if state\GetKey() == 'Entity' and IsValid(@GetEntity())
            @SetupEntity(@GetEntity())

        if state\GetKey() == 'Fly' and @flightController
            @flightController\Switch(state\GetValue())

        if state\GetKey() == 'PonySize'
            @ModifyScale()

        if state\GetKey() == 'DisableTask'
            @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]

        @GetBodygroupController()\DataChanges(state) if @ent and @GetBodygroupController()

        if CLIENT and @ent
            @GetWeightController()\DataChanges(state) if @GetWeightController()
            @GetRenderController()\DataChanges(state) if @GetRenderController()

    @STEP_SIZE = 18
    @PONY_HULL = 19
    @HULL_MINS = Vector(-@PONY_HULL, -@PONY_HULL, 0)
    @HULL_MAXS = Vector(@PONY_HULL, @PONY_HULL, 72 * PPM2.PONY_HEIGHT_MODIFIER)
    @HULL_MAXS_DUCK = Vector(@PONY_HULL, @PONY_HULL, 36 * PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL)

    @DEFAULT_HULL_MINS = Vector(-16, -16, 0)
    @DEFAULT_HULL_MAXS = Vector(16, 16, 72)
    @DEFAULT_HULL_MAXS_DUCK = Vector(16, 16, 36)
    @DEF_SCALE = Vector(1, 1, 1)

    ResetScale: =>
        return if not @ent
        @ent\ResetHull() if @ent.ResetHull
        @ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET_ORIGINAL) if @ent.SetViewOffset
        @ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL) if @ent.SetViewOffsetDucked
        @ent\SetStepSize(@@STEP_SIZE) if @ent.SetStepSize

        if SERVER and @ent.SetJumpPower and @ent.__ppm2_modified_jump
            @ent\SetJumpPower(@ent\GetJumpPower() / PPM2.PONY_JUMP_MODIFIER)
            @ent.__ppm2_modified_jump = false

        if CLIENT
            mat = Matrix()
            mat\Scale(@@DEF_SCALE)
            @ent\EnableMatrix('RenderMultiply', mat)
    ModifyScale: =>
        return if not @ent
        return if not @ent\IsPony()
        size = @GetPonySize()
        @ent\SetHull(@@HULL_MINS * size, @@HULL_MAXS * size) if @ent.SetHull
        @ent\SetHullDuck(@@HULL_MINS * size, @@HULL_MAXS_DUCK * size) if @ent.SetHullDuck
        @ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET * size) if @ent.SetViewOffset
        @ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK * size) if @ent.SetViewOffsetDucked
        @ent\SetStepSize(@@STEP_SIZE * size) if @ent.SetStepSize

        if SERVER and @ent.SetJumpPower and not @ent.__ppm2_modified_jump
            @ent\SetJumpPower(@ent\GetJumpPower() * PPM2.PONY_JUMP_MODIFIER)
            @ent.__ppm2_modified_jump = true

        if CLIENT
            mat = Matrix()
            mat\Scale(@@DEF_SCALE * size)
            @ent\EnableMatrix('RenderMultiply', mat)
    Reset: =>
        @ResetScale()
        @ModifyScale()
        if CLIENT
            @GetWeightController()\Reset() if @GetWeightController().Reset
            @GetRenderController()\Reset() if @GetRenderController().Reset
            @GetBodygroupController()\Reset() if @GetBodygroupController().Reset
    PlayerRespawn: =>
        return if not IsValid(@ent)
        @ent.__cachedIsPony = @ent\IsPony()
        if not @ent.__cachedIsPony
            return if @alreadyCalledRespawn
            @alreadyCalledRespawn = true
            @alreadyCalledDeath = true
        else
            @alreadyCalledRespawn = false
            @alreadyCalledDeath = false
        @ApplyBodygroups(CLIENT)
        @SetFly(false) if SERVER

        @ResetScale()
        @ModifyScale()

        if CLIENT
            @deathRagdollMerged = false
            @GetWeightController()\UpdateWeight() if @GetWeightController()
            @GetRenderController()\PlayerRespawn() if @GetRenderController()
            @GetBodygroupController()\MergeModels(@ent) if IsValid(@ent) and @GetBodygroupController().MergeModels

    PlayerDeath: =>
        return if not IsValid(@ent)
        @ent.__cachedIsPony = @ent\IsPony()
        if not @ent.__cachedIsPony
            return if @alreadyCalledDeath
            @alreadyCalledDeath = true
        else
            @alreadyCalledDeath = false
        @SetFly(false) if SERVER
        @ResetScale()
        if CLIENT
            @DoRagdollMerge()
            @GetRenderController()\PlayerDeath() if @GetRenderController()

    DoRagdollMerge: =>
        return if @deathRagdollMerged
        bgController = @GetBodygroupController()
        rag = @ent\GetRagdollEntity()
        if not bgController.MergeModels
            @deathRagdollMerged = true
        elseif IsValid(rag)
            @deathRagdollMerged = true
            bgController\MergeModels(rag)

    ApplyBodygroups: (updateModels = CLIENT) => @GetBodygroupController()\ApplyBodygroups(updateModels) if @ent
    SetLocalChange: (state) => @GenericDataChange(state)
    NetworkDataChanges: (state) => @GenericDataChange(state)
    SlowUpdate: => @GetBodygroupController()\SlowUpdate() if @GetBodygroupController()

    GetFlightController: => @flightController
    GetRenderController: =>
        return if SERVER
        return @renderController if not @isValid
        if not @renderController or @modelCached ~= @modelRender
            @modelRender = @modelCached
            cls = PPM2.GetRenderController(@modelCached)
            if @renderController and cls == @renderController.__class
                @renderController.ent = @ent
                PPM2.DebugPrint('Skipping render controller recreation for ', @ent, ' as part of ', @)
                return @renderController
            @renderController\Remove() if @renderController
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    GetWeightController: =>
        return if SERVER
        return @weightController if not @isValid
        if not @weightController or @modelCached ~= @modelWeight
            @modelCached = @modelCached or @ent\GetModel()
            @modelWeight = @modelCached
            cls = PPM2.GetPonyWeightController(@modelCached)
            if @weightController and cls == @weightController.__class
                @weightController.ent = @ent
                PPM2.DebugPrint('Skipping weight controller recreation for ', @ent, ' as part of ', @)
                return @weightController
            @weightController\Remove() if @weightController
            @weightController = cls(@)
        @weightController.ent = @ent
        return @weightController
    GetBodygroupController: =>
        return @bodygroups if not @isValid
        if not @bodygroups or @modelBodygroups ~= @modelCached
            @modelCached = @modelCached or @ent\GetModel()
            @modelBodygroups = @modelCached
            cls = PPM2.GetBodygroupController(@modelCached)
            if @bodygroups and cls == @bodygroups.__class
                @bodygroups.ent = @ent
                PPM2.DebugPrint('Skipping bodygroup controller recreation for ', @ent, ' as part of ', @)
                return @bodygroups
            @bodygroups\Remove() if @bodygroups
            @bodygroups = cls(@)
        @bodygroups.ent = @ent
        return @bodygroups
    Remove: (byClient = false) =>
        @@NW_Objects[@netID] = nil if @NETWORKED
        @isValid = false
        @ent = @GetEntity() if not IsValid(@ent)
        @ent.__PPM2_PonyData = nil if IsValid(@ent) and @ent.__PPM2_PonyData == @
        if CLIENT
            @GetWeightController()\Remove() if @GetWeightController()
            @GetRenderController()\Remove() if @GetRenderController()
            if IsValid(@ent) and @ent.__ppm2_task_hit
                @ent.__ppm2_task_hit = false
                @ent\SetNoDraw(false)
        @ResetScale() if IsValid(@ent)
        @GetBodygroupController()\Remove() if @GetBodygroupController()
        @flightController\Switch(false) if @flightController
        @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]
    __tostring: => "[#{@@__name}:#{@netID}|#{@ent}]"

PPM2.NetworkedPonyData = NetworkedPonyData

if CLIENT
    net.Receive 'PPM2.NotifyDisconnect', ->
        netID = net.ReadUInt(16)
        data = NetworkedPonyData.NW_Objects[netID]
        return if not data
        data\Remove()

    net.Receive 'PPM2.PonyDataRemove', ->
        netID = net.ReadUInt(16)
        data = NetworkedPonyData.NW_Objects[netID]
        return if not data
        data\Remove()

	hook.Add 'NetworkEntityCreated', 'PPM2.NetworkedObjectCheck', =>
		return if @GetPonyData()
		--for i, obj in pairs NetworkedPonyData.NW_Objects
        --    obj.ent =

entMeta = FindMetaTable('Entity')
entMeta.GetPonyData = =>
    if @__PPM2_PonyData and StrongEntity(@__PPM2_PonyData\GetEntity()) ~= StrongEntity(@)
        @__PPM2_PonyData\SetEntity(@)
        @__PPM2_PonyData\SetupEntity(@) if CLIENT
    return @__PPM2_PonyData

--
-- Copyright (C) 2017 DBot
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--

for ply in *player.GetAll()
    ply.__PPM2_PonyData\Remove() if ply.__PPM2_PonyData

class NetworkedPonyData extends PPM2.NetworkedObject
    @NW_ClientsideCreation = true
    @RenderTasks = {}

    @Setup()
    @NetworkVar('Entity',           net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), ((newValue) => IsValid(@GetOwner()) and StrongEntity(@GetOwner()\EntIndex()) or newValue))
    @NetworkVar('UpperManeModel',   net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('LowerManeModel',   net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('TailModel',        net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)
    @NetworkVar('SocksModel',       net.ReadStrongEntity, net.WriteStrongEntity, StrongEntity(-1), nil, false)

    @NetworkVar('Race',             (-> math.Clamp(net.ReadUInt(4), 0, 3)), ((arg = PPM2.RACE_EARTH) -> net.WriteUInt(arg, 4)), PPM2.RACE_EARTH)
    @NetworkVar('Gender',           (-> math.Clamp(net.ReadUInt(4), 0, 1)), ((arg = PPM2.GENDER_FEMALE) -> net.WriteUInt(arg, 4)), PPM2.GENDER_FEMALE)
    @NetworkVar('Weight',           (-> math.Clamp(net.ReadFloat(), PPM2.MIN_WEIGHT, PPM2.MAX_WEIGHT)), net.WriteFloat, 1)
    @NetworkVar('PonySize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_SCALE, PPM2.MAX_SCALE)),   net.WriteFloat, 1)

    -- Reserved - they can be accessed/used/changed, but they do not do anything
    @NetworkVar('Age',              (-> math.Clamp(net.ReadUInt(4), 0, 2)), ((arg = PPM2.AGE_ADULT) -> net.WriteUInt(arg, 4)), PPM2.AGE_ADULT)

    @NetworkVar('EyelashType',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYELASHES, PPM2.MAX_EYELASHES)),           ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('TailType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS, PPM2.MAX_TAILS)),                   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeType',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES, PPM2.MAX_UPPER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLower',    (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES, PPM2.MAX_LOWER_MANES)),       ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('TailTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_TAILS_NEW, PPM2.MAX_TAILS_NEW)),               ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeNew',      (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_UPPER_MANES_NEW, PPM2.MAX_UPPER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('ManeTypeLowerNew', (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_LOWER_MANES_NEW, PPM2.MAX_LOWER_MANES_NEW)),   ((arg = 0) -> net.WriteUInt(arg, 8)), 0)

    @NetworkVar('BodyColor',        net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))

    @NetworkVar('SeparateEyes',     net.ReadBool, net.WriteBool,              false)
    for publicName in *{'', 'Left', 'Right'}
        @NetworkVar("EyeType#{publicName}",          (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_EYE_TYPE, PPM2.MAX_EYE_TYPE)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("EyeBackground#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeHole#{publicName}",          net.ReadColor, net.WriteColor, 	    Color(0,   0,   0  ))
        @NetworkVar("EyeIrisTop#{publicName}",       net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisBottom#{publicName}",    net.ReadColor, net.WriteColor, 	    Color(200, 200, 200))
        @NetworkVar("EyeIrisLine1#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeIrisLine2#{publicName}",     net.ReadColor, net.WriteColor, 	    Color(255, 255, 255))
        @NetworkVar("EyeReflection#{publicName}",    net.ReadColor, net.WriteColor,    Color(255, 255, 255, 127))
        @NetworkVar("EyeEffect#{publicName}",        net.ReadColor, net.WriteColor,         Color(255, 255, 255))
        @NetworkVar("EyeLines#{publicName}",         net.ReadBool, net.WriteBool,                           true)
        @NetworkVar("DerpEyes#{publicName}",         net.ReadBool, net.WriteBool,                          false)
        @NetworkVar("DerpEyesStrength#{publicName}", (-> math.Clamp(net.ReadFloat(), PPM2.MIN_DERP_STRENGTH, PPM2.MAX_DERP_STRENGTH)), net.WriteFloat, 1)
        @NetworkVar("HoleWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat,  1)
        @NetworkVar("HoleHeight#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_PUPIL_SIZE, PPM2.MAX_PUPIL_SIZE)), net.WriteFloat,  1)
        @NetworkVar("HoleSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE, PPM2.MAX_HOLE)),             net.WriteFloat, .8)
        @NetworkVar("HoleShiftX#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)), net.WriteFloat, 0)
        @NetworkVar("HoleShiftY#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_HOLE_SHIFT, PPM2.MAX_HOLE_SHIFT)), net.WriteFloat, 0)
        @NetworkVar("IrisSize#{publicName}",         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, .8)
        @NetworkVar("IrisWidth#{publicName}",        (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, 1)
        @NetworkVar("IrisHeight#{publicName}",       (-> math.Clamp(net.ReadFloat(), PPM2.MIN_IRIS, PPM2.MAX_IRIS)),             net.WriteFloat, 1)
        @NetworkVar("EyeRotation#{publicName}",      (-> math.Clamp(net.ReadInt(12), PPM2.MIN_EYE_ROTATION, PPM2.MAX_EYE_ROTATION)), ((arg = 0) -> net.WriteInt(arg, 12)), 1)
        @NetworkVar("EyeURL#{publicName}",           net.ReadString,                                                            net.WriteString, '')

    @NetworkVar('SeparateMane',     net.ReadBool, net.WriteBool,              false)
    for i = 1, 6
        @NetworkVar("ManeColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("ManeURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("TailColor#{i}",            net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailDetailColor#{i}",      net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURLColor#{i}",         net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("TailURL#{i}",              net.ReadString, net.WriteString,   '')

        @NetworkVar("LowerManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("LowerManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("LowerManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))

        @NetworkVar("UpperManeColor#{i}",       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURL#{i}",         net.ReadString, net.WriteString,   '')
        @NetworkVar("UpperManeDetailColor#{i}", net.ReadColor, net.WriteColor,     Color(255, 255, 255))
        @NetworkVar("UpperManeURLColor#{i}",    net.ReadColor, net.WriteColor,     Color(255, 255, 255))

    @NetworkVar('CMark',            net.ReadBool, net.WriteBool,              true)
    @NetworkVar('CMarkSize',        (-> math.Clamp(net.ReadFloat(), 0, 1)), net.WriteFloat, 1)
    @NetworkVar('CMarkColor',       net.ReadColor, net.WriteColor,     Color(255, 255, 255))
    @NetworkVar('CMarkURL',         net.ReadString, net.WriteString,            '')
    @NetworkVar('CMarkType',        (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_CMARK, PPM2.MAX_CMARK)),           ((arg = 4) -> net.WriteUInt(arg, 8)), 4)
    @NetworkVar('TailSize',         (-> math.Clamp(net.ReadFloat(), PPM2.MIN_TAIL_SIZE, PPM2.MAX_TAIL_SIZE)),   net.WriteFloat, 1)

    @NetworkVar('Bodysuit',         (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SUIT, PPM2.MAX_SUIT)),             ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('Socks',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('NoFlex',           net.ReadBool, net.WriteBool,              false)

    @NetworkVar('SocksAsModel',     net.ReadBool, net.WriteBool,              false)
    @NetworkVar('SocksTexture',     (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_SOCKS, PPM2.MAX_SOCKS)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('SocksColor',       net.ReadColor, net.WriteColor,            Color(255, 255, 255))
    @NetworkVar('SocksTextureURL',  net.ReadString, net.WriteString,          '')

    for i = 1, 6
        @NetworkVar('SocksDetailColor' .. i, net.ReadColor, net.WriteColor, Color(255, 255, 255))

    @NetworkVar('BatPonyEars',      net.ReadBool, net.WriteBool,              false)
    @NetworkVar('Fangs',            net.ReadBool, net.WriteBool,              false)
    @NetworkVar('ClawTeeth',        net.ReadBool, net.WriteBool,              false)

    @NetworkVar('TeethColor',       net.ReadColor, net.WriteColor,    Color(255, 255, 255))
    @NetworkVar('MouthColor',       net.ReadColor, net.WriteColor,    Color(219, 65, 155))
    @NetworkVar('TongueColor',      net.ReadColor, net.WriteColor,    Color(235, 131, 59))

    for {:flex, :active} in *PPM2.PonyFlexController.FLEX_LIST
        @NetworkVar("DisableFlex#{flex}", net.ReadBool, net.WriteBool, false) if active

    for i = 1, PPM2.MAX_BODY_DETAILS
        @NetworkVar("BodyDetail#{i}",       (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_DETAIL, PPM2.MAX_DETAIL)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
        @NetworkVar("BodyDetailColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BodyDetailURL#{i}",    net.ReadString, net.WriteString, '')
        @NetworkVar("BodyDetailURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    for i = 1, 3
        @NetworkVar("HornURL#{i}",       net.ReadString, net.WriteString, '')
        @NetworkVar("WingsURL#{i}",      net.ReadString, net.WriteString, '')
        @NetworkVar("HornURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("WingsURLColor#{i}", net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    @NetworkVar('Fly',                  net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('DisableTask',          net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('UseFlexLerp',          net.ReadBool,   net.WriteBool,                  true)
    @NetworkVar('FlexLerpMultiplier',   net.ReadFloat,  net.WriteFloat,                    1)
    @NetworkVar('NewMuzzle',            net.ReadBool,   net.WriteBool,                  true)
    @NetworkVar('SeparateWings',        net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('SeparateHorn',         net.ReadBool,   net.WriteBool,                 false)
    @NetworkVar('WingsColor',           net.ReadColor,  net.WriteColor,  Color(255, 255, 255))
    @NetworkVar('HornColor',            net.ReadColor,  net.WriteColor,  Color(255, 255, 255))

    @NetworkVar('WingsType',            (-> math.Clamp(net.ReadUInt(8), PPM2.MIN_WINGS, PPM2.MAX_WINGS)), ((arg = 0) -> net.WriteUInt(arg, 8)), 0)
    @NetworkVar('MaleBuff',             (-> math.Clamp(net.ReadFloat(), PPM2.MIN_MALE_BUFF, PPM2.MAX_MALE_BUFF)), net.WriteFloat, PPM2.DEFAULT_MALE_BUFF)

    @NetworkVar('BatWingColor',         net.ReadColor, net.WriteColor,    Color(255, 255, 255))
    @NetworkVar('BatWingSkinColor',     net.ReadColor, net.WriteColor,    Color(255, 255, 255))

    for i = 1, 3
        @NetworkVar("BatWingURL#{i}",           net.ReadString, net.WriteString, '')
        @NetworkVar("BatWingSkinURL#{i}",       net.ReadString, net.WriteString, '')
        @NetworkVar("BatWingURLColor#{i}",      net.ReadColor,  net.WriteColor, Color(255, 255, 255))
        @NetworkVar("BatWingSkinURLColor#{i}",  net.ReadColor,  net.WriteColor, Color(255, 255, 255))

    Clone: (target = @ent) =>
        copy = @@(nil, target)
        @ApplyDataToObject(copy)
        return copy

    new: (netID, ent) =>
        @recomputeTextures = true
        @isValid = true
        if ent
            @modelCached = ent\GetModel()
            @SetEntity(ent)
            @SetupEntity(ent)
        super(netID)
    IsValid: => @isValid
    GetModel: => @modelCached
    EntIndex: => @entID
    SetupEntity: (ent) =>
        if ent.__PPM2_PonyData
            return if ent.__PPM2_PonyData\GetOwner() ~= @GetOwner()
            ent.__PPM2_PonyData\Remove() if ent.__PPM2_PonyData.Remove and ent.__PPM2_PonyData ~= @
        ent.__PPM2_PonyData = @
        @ent = ent
        return unless IsValid(ent)
        @modelCached = ent\GetModel()
        @ent = ent
        @flightController = PPM2.PonyflyController(@)
        @entID = ent\EntIndex()
        @ModelChanges(@modelCached, @modelCached)
        @Reset()
        if CLIENT
            timer.Simple 0, ->
                @GetRenderController()\CompileTextures() if @GetRenderController()
        PPM2.DebugPrint('Ponydata ', @, ' was updated to use for ', @ent)
        @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]
    ModelChanges: (old = @ent\GetModel(), new = old) =>
        @modelCached = new
        @SetFly(false) if SERVER
        timer.Simple 0.5, ->
            return unless IsValid(@ent)
            @GetBodygroupController()\ApplyBodygroups(CLIENT) if @GetBodygroupController()
            if CLIENT
                @GetRenderController()
                @GetWeightController()
            @Reset()
    GenericDataChange: (state) =>
        if state\GetKey() == 'Entity' and IsValid(@GetEntity())
            @SetupEntity(@GetEntity())

        if state\GetKey() == 'Fly' and @flightController
            @flightController\Switch(state\GetValue())

        if state\GetKey() == 'PonySize'
            @ModifyScale()

        if state\GetKey() == 'DisableTask'
            @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]

        @GetBodygroupController()\DataChanges(state) if @ent and @GetBodygroupController()

        if CLIENT and @ent
            @GetWeightController()\DataChanges(state) if @GetWeightController()
            @GetRenderController()\DataChanges(state) if @GetRenderController()

    @STEP_SIZE = 18
    @PONY_HULL = 19
    @HULL_MINS = Vector(-@PONY_HULL, -@PONY_HULL, 0)
    @HULL_MAXS = Vector(@PONY_HULL, @PONY_HULL, 72 * PPM2.PONY_HEIGHT_MODIFIER)
    @HULL_MAXS_DUCK = Vector(@PONY_HULL, @PONY_HULL, 36 * PPM2.PONY_HEIGHT_MODIFIER_DUCK_HULL)

    @DEFAULT_HULL_MINS = Vector(-16, -16, 0)
    @DEFAULT_HULL_MAXS = Vector(16, 16, 72)
    @DEFAULT_HULL_MAXS_DUCK = Vector(16, 16, 36)
    @DEF_SCALE = Vector(1, 1, 1)

    ResetScale: =>
        return if not @ent
        @ent\ResetHull() if @ent.ResetHull
        @ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET_ORIGINAL) if @ent.SetViewOffset
        @ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK_ORIGINAL) if @ent.SetViewOffsetDucked
        @ent\SetStepSize(@@STEP_SIZE) if @ent.SetStepSize

        if @ent.SetJumpPower and @ent.__ppm2_modified_jump
            @ent\SetJumpPower(@ent\GetJumpPower() / PPM2.PONY_JUMP_MODIFIER)
            @ent.__ppm2_modified_jump = false

        if CLIENT
            mat = Matrix()
            mat\Scale(@@DEF_SCALE)
            @ent\EnableMatrix('RenderMultiply', mat)
    ModifyScale: =>
        return if not @ent
        return if not @ent\IsPony()
        size = @GetPonySize()
        @ent\SetHull(@@HULL_MINS * size, @@HULL_MAXS * size) if @ent.SetHull
        @ent\SetHullDuck(@@HULL_MINS * size, @@HULL_MAXS_DUCK * size) if @ent.SetHullDuck
        @ent\SetViewOffset(PPM2.PLAYER_VIEW_OFFSET * size) if @ent.SetViewOffset
        @ent\SetViewOffsetDucked(PPM2.PLAYER_VIEW_OFFSET_DUCK * size) if @ent.SetViewOffsetDucked
        @ent\SetStepSize(@@STEP_SIZE * size) if @ent.SetStepSize

        if @ent.SetJumpPower and not @ent.__ppm2_modified_jump
            @ent\SetJumpPower(@ent\GetJumpPower() * PPM2.PONY_JUMP_MODIFIER)
            @ent.__ppm2_modified_jump = true

        if CLIENT
            mat = Matrix()
            mat\Scale(@@DEF_SCALE * size)
            @ent\EnableMatrix('RenderMultiply', mat)
    Reset: =>
        @ResetScale()
        @ModifyScale()
        if CLIENT
            @GetWeightController()\Reset() if @GetWeightController().Reset
            @GetRenderController()\Reset() if @GetRenderController().Reset
            @GetBodygroupController()\Reset() if @GetBodygroupController().Reset
    PlayerRespawn: =>
        return if not IsValid(@ent)
        @ent.__cachedIsPony = @ent\IsPony()
        if not @ent.__cachedIsPony
            return if @alreadyCalledRespawn
            @alreadyCalledRespawn = true
            @alreadyCalledDeath = true
        else
            @alreadyCalledRespawn = false
            @alreadyCalledDeath = false
        @ApplyBodygroups(CLIENT)
        @SetFly(false) if SERVER

        @ResetScale()
        @ModifyScale()

        if CLIENT
            @deathRagdollMerged = false
            @GetWeightController()\UpdateWeight() if @GetWeightController()
            @GetRenderController()\PlayerRespawn() if @GetRenderController()
            @GetBodygroupController()\MergeModels(@ent) if IsValid(@ent) and @GetBodygroupController().MergeModels

    PlayerDeath: =>
        return if not IsValid(@ent)
        @ent.__cachedIsPony = @ent\IsPony()
        if not @ent.__cachedIsPony
            return if @alreadyCalledDeath
            @alreadyCalledDeath = true
        else
            @alreadyCalledDeath = false
        @SetFly(false) if SERVER
        @ResetScale()
        if CLIENT
            @DoRagdollMerge()
            @GetRenderController()\PlayerDeath() if @GetRenderController()

    DoRagdollMerge: =>
        return if @deathRagdollMerged
        bgController = @GetBodygroupController()
        rag = @ent\GetRagdollEntity()
        if not bgController.MergeModels
            @deathRagdollMerged = true
        elseif IsValid(rag)
            @deathRagdollMerged = true
            bgController\MergeModels(rag)

    ApplyBodygroups: (updateModels = CLIENT) => @GetBodygroupController()\ApplyBodygroups(updateModels) if @ent
    SetLocalChange: (state) => @GenericDataChange(state)
    NetworkDataChanges: (state) => @GenericDataChange(state)
    SlowUpdate: => @GetBodygroupController()\SlowUpdate() if @GetBodygroupController()

    GetFlightController: => @flightController
    GetRenderController: =>
        return if SERVER
        return @renderController if not @isValid
        if not @renderController or @modelCached ~= @modelRender
            @modelRender = @modelCached
            cls = PPM2.GetRenderController(@modelCached)
            if @renderController and cls == @renderController.__class
                @renderController.ent = @ent
                PPM2.DebugPrint('Skipping render controller recreation for ', @ent, ' as part of ', @)
                return @renderController
            @renderController\Remove() if @renderController
            @renderController = cls(@)
        @renderController.ent = @ent
        return @renderController
    GetWeightController: =>
        return if SERVER
        return @weightController if not @isValid
        if not @weightController or @modelCached ~= @modelWeight
            @modelCached = @modelCached or @ent\GetModel()
            @modelWeight = @modelCached
            cls = PPM2.GetPonyWeightController(@modelCached)
            if @weightController and cls == @weightController.__class
                @weightController.ent = @ent
                PPM2.DebugPrint('Skipping weight controller recreation for ', @ent, ' as part of ', @)
                return @weightController
            @weightController\Remove() if @weightController
            @weightController = cls(@)
        @weightController.ent = @ent
        return @weightController
    GetBodygroupController: =>
        return @bodygroups if not @isValid
        if not @bodygroups or @modelBodygroups ~= @modelCached
            @modelCached = @modelCached or @ent\GetModel()
            @modelBodygroups = @modelCached
            cls = PPM2.GetBodygroupController(@modelCached)
            if @bodygroups and cls == @bodygroups.__class
                @bodygroups.ent = @ent
                PPM2.DebugPrint('Skipping bodygroup controller recreation for ', @ent, ' as part of ', @)
                return @bodygroups
            @bodygroups\Remove() if @bodygroups
            @bodygroups = cls(@)
        @bodygroups.ent = @ent
        return @bodygroups
    Remove: (byClient = false) =>
        @@NW_Objects[@netID] = nil if @NETWORKED
        @isValid = false
        @ent = @GetEntity() if not IsValid(@ent)
        @ent.__PPM2_PonyData = nil if IsValid(@ent) and @ent.__PPM2_PonyData == @
        if CLIENT
            @GetWeightController()\Remove() if @GetWeightController()
            @GetRenderController()\Remove() if @GetRenderController()
            if IsValid(@ent) and @ent.__ppm2_task_hit
                @ent.__ppm2_task_hit = false
                @ent\SetNoDraw(false)
        @ResetScale() if IsValid(@ent)
        @GetBodygroupController()\Remove() if @GetBodygroupController()
        @flightController\Switch(false) if @flightController
        @@RenderTasks = [task for i, task in pairs @@NW_Objects when task\IsValid() and IsValid(task.ent) and not task.ent\IsPlayer() and not task\GetDisableTask()]
    __tostring: => "[#{@@__name}:#{@netID}|#{@ent}]"

PPM2.NetworkedPonyData = NetworkedPonyData

if CLIENT
    net.Receive 'PPM2.NotifyDisconnect', ->
        netID = net.ReadUInt(16)
        data = NetworkedPonyData.NW_Objects[netID]
        return if not data
        data\Remove()

    net.Receive 'PPM2.PonyDataRemove', ->
        netID = net.ReadUInt(16)
        data = NetworkedPonyData.NW_Objects[netID]
        return if not data
        data\Remove()

	hook.Add 'NetworkEntityCreated', 'PPM2.NetworkedObjectCheck', =>
		return if @GetPonyData()
		--for i, obj in pairs NetworkedPonyData.NW_Objects
        --    obj.ent =

entMeta = FindMetaTable('Entity')
entMeta.GetPonyData = =>
    if @__PPM2_PonyData and StrongEntity(@__PPM2_PonyData\GetEntity()) ~= StrongEntity(@)
        @__PPM2_PonyData\SetEntity(@)
        @__PPM2_PonyData\SetupEntity(@) if CLIENT
    return @__PPM2_PonyData


github: use compare URL instead of URL to individual commit

Former-commit-id: 0280ad85563034ef01a169f317c799752f6d2378 [formerly 573f793ca2e6e93df7c014f69c65747f72cdcd87]
Former-commit-id: 57c07f35a3adacb8ead399293706b9b26efc1b79

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      if i == 1
        message = message .. ' ' .. json.compare
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message}"
    if #json.commits > 3
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = unescape(destination)
  json = util.json.decode(req.body)

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}

util = require 'util'

hex_to_char = (x) ->
  string.char(tonumber(x, 16))

unescape = (url) ->
  url\gsub("%%(%x%x)", hex_to_char)

handlers = {
  push: (repo, destination, json) ->
    branch = util.bold(json.ref\gsub 'refs/heads/', '')
    for i, c in pairs(json.commits)
      if i > 3
        break
      message = c.message\gsub '\n.*', ''
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{util.nonickalertword c.author.username}: #{message} #{c.url}"
    if #json.commits > 3
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{branch}, #{#json.commits-3} more commits not displayed."
  watch: (repo, destination, json) ->
    action = json.action
    if action == 'started'
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: +1 ★ (Total: #{json.repository.stargazers_count})"
    else
      ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} watching"
  ping: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: ping"
  commit_comment: (repo, destination, json) ->
    --commenter = json.comment.user.login
    body = json.comment.body
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: commented #{body}"
  pull_request: (repo, destination, json) ->
    action = json.action
    number = util.bold json.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} PR ##{number}: #{json.pull_request.title} #{json.pull_request.html_url}"
  issues: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: #{action} issue ##{nr}: #{json.issue.title} #{json.issue.html_url}"
  issue_comment: (repo, destination, json) ->
    action = json.action
    nr = util.bold json.issue.number
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: comment on issue ##{nr}: #{json.comment.body} #{json.issue.html_url}"
  fork: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: forked to #{json.forkee.html_url}"
  create: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: created #{json.ref_type} #{util.bold json.ref}"
  delete: (repo, destination, json) ->
    ivar2\Msg 'privmsg', destination, nil, "[#{repo}]: #{util.nonickalertword json.sender.login}: deleted #{json.ref_type} #{util.bold json.ref}"
  gollum: (repo, destination, json) ->
    return
  release: (repo, destination, json) ->
    return
}

ivar2.webserver.regUrl '/github/(.*)', (req, res) ->
  destination = req.url\match('/github/(.+)/?$')
  destination = unescape(destination)
  json = util.json.decode(req.body)

  repo = json.repository.full_name

  event = req.headers['X-GitHub-Event']
  handler = handlers[event]
  if handler
    handler(repo, destination, json)
  else
    ivar2\Log('error', 'Unknown github event: %s', event)

  res\set_status(200)
  res\set_header('Content-Type', 'text/plain')
  res\set_body('ok')
  res\send()

return {}


views/welcome: note that file can be removed

import Widget from require "lapis.html"

class Welcome extends Widget
  content: =>
    div class: "body", ->
      p @_("Welcome to Kodomo Quiz!")
      element 'ol', ->
        element 'li', ->
          text @_("Please, create file ")
          b @session.filename
          raw @_(" in your <b>public_html</b>")
          text @_(" and write the following text: ")
          b @session.token
        element 'li', ->
          form method: "POST", action: @url_for("login"), ->
            input {type: "hidden", name: "csrf_token",
                value: @new_csrf}
            text @_("Your username: ")
            input type: "text", name: "user"
            text ' '
            input type: "submit", value: @_("Login")
        element 'li', ->
          text @_[[Then you can remove the file.
            It is checked only when you press
            the button above.]]