More test code

More test code

--
-- 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.
--

class NetworkChangeState
	new: (key = '', newValue, obj, len = 24, ply = NULL) =>
		@key = key
		@oldValue = obj[key]
		@newValue = newValue
		@ply = ply
		@time = CurTime()
		@rtime = RealTime()
		@stime = SysTime()
		@obj = obj
		@objID = obj.netID
		@len = len
		@rlen = len - 24 -- ID - 16 bits, variable id - 8 bits
	GetPlayer: => @ply
	ChangedByClient: => IsValid(@ply)
	ChangedByPlayer: => IsValid(@ply)
	GetKey: => @key
	GetVariable: => @key
	GetVar: => @key
	GetNewValue: => @newValue
	NewValue: => @newValue
	GetOldValue: => @oldValue
	OldValue: => @oldValue
	CurTime: => @time
	GetCurTime: => @time
	GetReceiveTime: => @time
	GetReceiveStamp: => @time
	RealTime: => @rtime
	GetRealTime: => @rtime
	SysTime: => @stime
	GetSysTime: => @stime
	GetObject: => @obj
	GetNWObject: => @obj
	GetNetworkedObject: => @obj
	GetLength: => @rlen
	GetRealLength: => @len

	Revert: => @obj[@key] = @oldValue
	Apply: => @obj[@key] = @newValue

class NetworkedObject
	@Setup = =>
		@NW_Vars = {}
		@NW_VarsTable = {}
		@NW_Objects = {}
		@NW_Setup = true
		@NW_NextVarID = -1
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_NextObjectID = -1

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) -> @OnNetworkedModify(ply, len)
	@__inherited = (child) => child.Setup(child)
	@Setup()

	@AddNetworkVar = (strName = 'var', readFunc = (->), writeFunc = (->)) =>
		@NW_NextVarID += 1
		tab = {:strName, :readFunc, :writeFunc, id: @NW_NextVarID}
		table.insert(@NW_Vars, tab)
		@NW_VarsTable[@NW_NextVarID] = tab

	@NW_ClientsideCreation = false
	@NW_RemoveOnPlayerLeave = true
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		@OnNetworkedCreatedCallback(ply, len)
	@OnNetworkedCreatedCallback = (ply = NULL, len = 0) => -- Override

	@OnNetworkedModify = (ply = NULL, len = 0) =>
		return if not @NW_ClientsideCreation and IsValid(ply)
		id = net.ReadUInt(16)
		obj = @NW_Objects[id]
		return unless obj
		return if IsValid(ply) and obj.NW_Player ~= ply
		varID = net.ReadUInt(8)
		varData = @NW_VarsTable[varID]
		return unless varData
		{:strName, :readFunc} = varData
		newVal = readFunc()
		state = NetworkChangeState(strName, newVal, obj, len, ply)
		state\Apply()
		obj\NetworkDataChanges(state)
		@OnNetworkedModifyCallback(state)
	@OnNetworkedModifyCallback = (state) => -- Override

	@ReadNetworkData = =>
		output = {strName, readFunc() for {:strName, :readFunc} in *@NW_Vars}
		return output

	new: (netID = @@NW_NextObjectID) =>
		@valid = true

		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
		else
			@netID = netID

		@@NW_Objects[@netID] = @

	IsValid: => @valid
	Remove: =>
		@@NW_Objects[@netID] = nil
		@valid = false

	NetworkDataChanges: (state) => -- Override
	ReadNetworkData: (len = 24, ply = NULL, silent = false) =>
		data = @@ReadNetworkData()
		oldData = [{k, @[k], v} for k, v in pairs data]
		states = [NetworkChangeState(key, newVal, @, len, ply) for {key, oldVal, newVal} in *oldData]
		for state in *states
			state\Apply()
			@NetworkDataChanges(state) unless silent

PPM2.NetworkedObject = NetworkedObject

--
-- 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.
--

class NetworkedObject
	@Setup = =>
		@NW_Vars = {}
		@NW_Setup = true
		@NW_NextVarID = -1
		@NW_Create = "PPM2.NW.Created.#{@__name}"
		@NW_Modify = "PPM2.NW.Modified.#{@__name}"
		@NW_NextObjectID = -1

		if SERVER
			util.AddNetworkString(@NW_Create)
			util.AddNetworkString(@NW_Modify)

		net.Receive @NW_Create, (len = 0, ply = NULL) -> @OnNetworkedCreated(ply, len)
		net.Receive @NW_Modify, (len = 0, ply = NULL) ->

			@OnNetworkedModify(ply, len)

	@__inherited = (child) => child.Setup(child)
	@Setup()

	@AddNetworkVar = (strName = 'var', readFunc = (->), writeFunc = (->)) => table.insert(@NWVars, {:strName, :readFunc, :writeFunc})
	@OnNetworkedCreated = (ply = NULL, len = 0) =>
		-- Override
	@OnNetworkedModify = (ply = NULL, len = 0) =>
		-- Override

	@ReadNetworkData = =>

	new: (netID = @@NW_NextObjectID) =>
		if SERVER
			@netID = @@NW_NextObjectID
			@@NW_NextObjectID += 1
		else
			@netID = netID

PPM2.NetworkedObject = NetworkedObject


fixed a clear bug where we tried to import a non existent file

return (path) ->
    return {
        keyschedule: (require path .. '/aes/keyschedule')(path)
        steps: (require path .. '/aes/steps')(path)
        encrypt: (require path .. '/aes/encrypt')(path)
        decrypt: (require path .. '/aes/decrypt')(path)
        ecb: (require path .. '/aes/ecb')(path)
        cbc: (require path .. '/aes/cbc')(path)
        otherciphers: nil
    }

return (path) ->
    return {
        keyschedule: (require path .. '/aes/keyschedule')(path)
        steps: (require path .. '/aes/steps')(path)
        encrypt: (require path .. '/aes/encrypt')(path)
        decrypt: (require path .. '/aes/decrypt')(path)
        ecb: (require path .. '/aes/ecb')(path)
        cbc: (require path .. '/aes/cbc')(path)
        stringEncryption: (require path .. '/aes/stringwrapper')(path)
        otherciphers: nil
    }


gender

gender

html = require "lapis.html"

class ProfileEdit extends html.Widget
  mkCheck:(name,txt,val)=>
    label for:"c_"..name, class:"pure-checkbox", ->
      input type:"checkbox", id:"c_"..name, :name, checked:(val and "checked" or nil)
      text txt
  content: =>
    form action:@url_for("profile_edit"), class: "pure-form pure-form-stacked", method:"post", ->
      with @profiledata
        fieldset ->
          legend "Birthday"
          input id:"birthday", name:"birthday", type:"date", value: .birthday or ""
          fieldset ->
            legend "Show publicly:"
            div class: "pure-control-group", ->
              @mkCheck "privacy_birthday_dm", "Date & Month", .privacy_birthday_dm
              @mkCheck "privacy_birthday_y", "Year", .privacy_birthday_y
              @mkCheck "privacy_birthday_age", "Age in years", .privacy_birthday_age
        fieldset ->
          legend "Gender"
          input id:"gender", name:"gender", type:"text", list:"genderlist", value: .gender or ""
          datalist id:"genderlist", ->
            option value: "Agender"
            option value: "Female"
            option value: "Male"
            option value: "Gender Fluid"
            option value: "Intersex"
          text "Select or type custom, any capitalized letters will be used for abbreviation."
          fieldset ->
            legend "Show:"
            div class: "pure-control-group", ->
              @mkCheck "privacy_gender", "Publicly", .privacy_gender
        fieldset ->
          legend "About me"
          textarea id:"about", name:"about", ->
            raw .about
          text "This field has "
          a target:"_blank", href:"https://daringfireball.net/projects/markdown/syntax", "markdown support"
          text " but you may not use HTML for security reasons."
      div class: "pure-controls", ->
        input type:"submit", class: "pure-button pure-button-primary"

html = require "lapis.html"

class ProfileEdit extends html.Widget
  mkCheck:(name,txt,val)=>
    label for:"c_"..name, class:"pure-checkbox", ->
      input type:"checkbox", id:"c_"..name, :name, checked:(val and "checked" or nil)
      text txt
  content: =>
    form action:@url_for("profile_edit"), class: "pure-form pure-form-stacked", method:"post", ->
      with @profiledata
        fieldset ->
          legend "Birthday"
          input id:"birthday", name:"birthday", type:"date", value: .birthday or ""
          fieldset ->
            legend "Show publicly:"
            div class: "pure-control-group", ->
              @mkCheck "privacy_birthday_dm", "Date & Month", .privacy_birthday_dm
              @mkCheck "privacy_birthday_y", "Year", .privacy_birthday_y
              @mkCheck "privacy_birthday_age", "Age in years", .privacy_birthday_age
        fieldset ->
          legend "Gender"
          input id:"gender", name:"gender", type:"text", list:"genderlist", value: .gender or ""
          datalist id:"genderlist", ->
            option value: "Agender"
            option value: "Female"
            option value: "Male"
            option value: "Gender Fluid"
            option value: "Gender Queer"
            option value: "Intersex"
          text "Select or type custom, any capitalized letters will be used for abbreviation."
          fieldset ->
            legend "Show:"
            div class: "pure-control-group", ->
              @mkCheck "privacy_gender", "Publicly", .privacy_gender
        fieldset ->
          legend "About me"
          textarea id:"about", name:"about", ->
            raw .about
          text "This field has "
          a target:"_blank", href:"https://daringfireball.net/projects/markdown/syntax", "markdown support"
          text " but you may not use HTML for security reasons."
      div class: "pure-controls", ->
        input type:"submit", class: "pure-button pure-button-primary"


adding support for initializing dev

adding support for initializing dev

#!/usr/bin/env moon

-- Copyright (C) 2014 Sam Dodrill <xena@yolo-swag.com> All rights reserved.
--
-- This software is provided 'as-is', without any express or implied
-- warranty. In no event will the authors be held liable for any damages
-- arising from the use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute it
-- freely, subject to the following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
--    claim that you wrote the original software. If you use this software
--    in a product, an acknowledgment in the product documentation would be
--    appreciated but is not required.
--
-- 2. Altered source versions must be plainly marked as such, and must not be
--    misrepresented as being the original software.
--
-- 3. This notice may not be removed or altered from any source
--    distribution.

export yaml = require "yaml"

--- yaml2Table loads a yaml file as text and returns it as a file.
--  Returns a table representing the yaml document and an error string or nil.
yaml2Table = (fname) -> --> table, error
  fin = io.open fname, "r"
  if fin == nil
    return {}, "File read (#{fname}) failed."

  data = fin\read "*all"
  ret = yaml.load data

  fin\close!

  return ret, nil

--- doCommand returns the output and return status of a command.
doCommand = (command) -> --> string, number
  n = os.tmpname!
  code = os.execute command .. " > " .. n
  lines = {}

  for line in io.lines n
    table.insert lines, line

  os.remove n

  lines, code

--- pairsByKeys is an iterator for a table aplhabetically
--  adapted from here: http://www.lua.org/pil/19.3.html
pairsByKeys = (t) -> --> function
  a = {}
  for n in pairs t
    table.insert a, n

  table.sort a

  i = 0
  iter = ->
    i += 1
    if a[i] == nil
      return nil
    else
      return a[i], t[a[i]]
  iter

export commands = {

  init: {"Copies ~/.dev.yml to this directory", ->
    doCommand "cp ~/.dev.yaml ./"
  }

  up: {"Brings up a development container", ->
    dcommand = "docker run -idt --name #{data.projname}-dev --hostname dev:#{data.projname} "
    path = "/home/#{data.user}/dev/"
    localdir = os.getenv "PWD"

    -- Is this go?
    if data.golang
      path = "/home/#{data.user}/go/src/#{data.repopath}"

    -- Add source code directory to mount
    dcommand ..= "-v #{localdir}:#{path} "

    -- Add ssh keys if needed
    if data.ssh
      dcommand ..= "-v /home/#{os.getenv "USER"}/.ssh:/home/#{data.user}/.ssh "

    image = data.base
    if data.base == nil and data.overlay ~= nil
      image = "dev-#{data.projname}"

    -- Append image name
    dcommand ..= image

    print "Starting up container for #{data.projname}"

    -- Start up the docker container
    lines, status = doCommand dcommand
    if status ~= 0
      print "Launch failed. Does this container already exist?"
      for _,line in pairs lines
        print "docker: " .. line
      os.exit status

    ctid = lines[#lines]

    -- report to user
    print "#{data.projname}-dev (ctid: #{ctid\sub(1,12)}) running!"
    print "To use this container please attach to it with:"
    print "    $ docker attach #{data.projname}-dev"
  }

  down: { "Destroys a development container", ->
    lines, status = doCommand "docker rm -f #{data.projname}-dev"
    if status ~= 0
      os.exit status

    print "Container destroyed."
  }

  establish: { "Create a Docker image from the manifest", ->
    if data.overlay == nil
      print "Error: docker container overlay not made. Exiting."
      os.exit 1

    -- Create temporary folder for the build
    dir = os.tmpname!
    os.remove dir

    out, err = doCommand "mkdir #{dir}"
    if err ~= 0
      print "Could not make temporary directory #{dir}. Dying."
      os.exit err

    -- Output the overlay as a Dockerfile
    fout = io.open "#{dir}/Dockerfile", "w"
    fout\write data.overlay
    fout\close!

    -- Build the Docker image
    proc = io.popen "docker build -t dev-#{data.projname} #{dir}"
    for line in proc\lines!
      print line

    proc\close!

    -- Kill oof the temporary directory
    _, err = doCommand "rm -rf #{dir}"
    if err ~= 0
      os.exit err

    print "Docker image dev-#{data.projname} created."
  }

  purge: { "Delete a created overlayed image", ->
    if data.overlay == nil
      print "Cannot delete image without an overlay defined!"
      os.exit 1

    -- Kill the image
    _, err = doCommand "docker rmi -f dev-#{data.projname}"
    if err ~= 0
      print "There was an error."
      os.exit err

    print "Image deleted."
  }
}

if #arg == 0
  print "dev version 0.2\n"

  print "Usage: dev [command] <manifest>\n"

  print "  if manifest is undefined the default value"
  print "  .dev.yaml will be used.\n"

  print "Available commands:"
  for name,cmd in pairsByKeys commands
    print "%12s   %s"\format(name, cmd[1])

  os.exit 1

command = arg[1]\lower!

if commands[command] ~= nil
  export fname = arg[2]
  if fname == nil
    fname = ".dev.yaml"

  export data, err = yaml2Table fname
  if err ~= nil and command ~= 'init'
    print "Cannot open file #{fname}. Please make sure this file exists or is readable."
    os.exit 1

  commands[command][2]!

#!/usr/bin/env moon

-- Copyright (C) 2014 Sam Dodrill <xena@yolo-swag.com> All rights reserved.
--
-- This software is provided 'as-is', without any express or implied
-- warranty. In no event will the authors be held liable for any damages
-- arising from the use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute it
-- freely, subject to the following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
--    claim that you wrote the original software. If you use this software
--    in a product, an acknowledgment in the product documentation would be
--    appreciated but is not required.
--
-- 2. Altered source versions must be plainly marked as such, and must not be
--    misrepresented as being the original software.
--
-- 3. This notice may not be removed or altered from any source
--    distribution.

export yaml = require "yaml"

--- yaml2Table loads a yaml file as text and returns it as a file.
--  Returns a table representing the yaml document and an error string or nil.
yaml2Table = (fname) -> --> table, error
  fin = io.open fname, "r"
  if fin == nil
    return {}, "File read (#{fname}) failed."

  data = fin\read "*all"
  ret = yaml.load data

  fin\close!

  return ret, nil

--- doCommand returns the output and return status of a command.
doCommand = (command) -> --> string, number
  n = os.tmpname!
  code = os.execute command .. " > " .. n
  lines = {}

  for line in io.lines n
    table.insert lines, line

  os.remove n

  lines, code

--- pairsByKeys is an iterator for a table aplhabetically
--  adapted from here: http://www.lua.org/pil/19.3.html
pairsByKeys = (t) -> --> function
  a = {}
  for n in pairs t
    table.insert a, n

  table.sort a

  i = 0
  iter = ->
    i += 1
    if a[i] == nil
      return nil
    else
      return a[i], t[a[i]]
  iter

export commands = {
  up: {"Brings up a development container", ->
    dcommand = "docker run -idt --name #{data.projname}-dev --hostname dev:#{data.projname} "
    path = "/home/#{data.user}/dev/"
    localdir = os.getenv "PWD"

    -- Is this go?
    if data.golang
      path = "/home/#{data.user}/go/src/#{data.repopath}"

    -- Add source code directory to mount
    dcommand ..= "-v #{localdir}:#{path} "

    -- Add ssh keys if needed
    if data.ssh
      dcommand ..= "-v /home/#{os.getenv "USER"}/.ssh:/home/#{data.user}/.ssh "

    image = data.base
    if data.base == nil and data.overlay ~= nil
      image = "dev-#{data.projname}"

    -- Append image name
    dcommand ..= image

    print "Starting up container for #{data.projname}"

    -- Start up the docker container
    lines, status = doCommand dcommand
    if status ~= 0
      print "Launch failed. Does this container already exist?"
      for _,line in pairs lines
        print "docker: " .. line
      os.exit status

    ctid = lines[#lines]

    -- report to user
    print "#{data.projname}-dev (ctid: #{ctid\sub(1,12)}) running!"
    print "To use this container please attach to it with:"
    print "    $ docker attach #{data.projname}-dev"
  }

  down: { "Destroys a development container", ->
    lines, status = doCommand "docker rm -f #{data.projname}-dev"
    if status ~= 0
      os.exit status

    print "Container destroyed."
  }

  establish: { "Create a Docker image from the manifest", ->
    if data.overlay == nil
      print "Error: docker container overlay not made. Exiting."
      os.exit 1

    -- Create temporary folder for the build
    dir = os.tmpname!
    os.remove dir

    out, err = doCommand "mkdir #{dir}"
    if err ~= 0
      print "Could not make temporary directory #{dir}. Dying."
      os.exit err

    -- Output the overlay as a Dockerfile
    fout = io.open "#{dir}/Dockerfile", "w"
    fout\write data.overlay
    fout\close!

    -- Build the Docker image
    proc = io.popen "docker build -t dev-#{data.projname} #{dir}"
    for line in proc\lines!
      print line

    proc\close!

    -- Kill oof the temporary directory
    _, err = doCommand "rm -rf #{dir}"
    if err ~= 0
      os.exit err

    print "Docker image dev-#{data.projname} created."
  }

  purge: { "Delete a created overlayed image", ->
    if data.overlay == nil
      print "Cannot delete image without an overlay defined!"
      os.exit 1

    -- Kill the image
    _, err = doCommand "docker rmi -f dev-#{data.projname}"
    if err ~= 0
      print "There was an error."
      os.exit err

    print "Image deleted."
  }
}

if #arg == 0
  print "dev version 0.2\n"

  print "Usage: dev [command] <manifest>\n"

  print "  if manifest is undefined the default value"
  print "  .dev.yaml will be used.\n"

  print "Available commands:"
  for name,cmd in pairsByKeys commands
    print "%12s   %s"\format(name, cmd[1])

  os.exit 1

command = arg[1]\lower!

if commands[command] ~= nil
  export fname = arg[2]
  if fname == nil
    fname = ".dev.yaml"

  export data, err = yaml2Table fname
  if err ~= nil
    print "Cannot open file #{fname}. Please make sure this file exists or is readable."
    os.exit 1

  commands[command][2]!


Added PPM2_PonyDataChanges(NetworkedPonyData, Entity, NetworkChangeState) hook

--
-- 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

wUInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteUInt(arg, size)

wInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteInt(arg, size)

rUInt = (size = 8, min = 0, max = 255) ->
    return -> math.Clamp(net.ReadUInt(size), min, max)

rInt = (size = 8, min = -128, max = 127) ->
    return -> math.Clamp(net.ReadInt(size), min, max)

rFloat = (min = 0, max = 255) ->
    return -> math.Clamp(net.ReadFloat(), min, max)

wFloat = net.WriteFloat
rSEnt = net.ReadStrongEntity
wSEnt = net.WriteStrongEntity
rBool = net.ReadBool
wBool = net.WriteBool
rColor = net.ReadColor
wColor = net.WriteColor
rString = net.ReadString
wString = net.WriteString

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

    @Setup()
    @NetworkVar('Entity',           rSEnt, wSEnt, StrongEntity(-1), ((newValue) => IsValid(@GetOwner()) and StrongEntity(@GetOwner()\EntIndex()) or newValue))
    @NetworkVar('UpperManeModel',   rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('LowerManeModel',   rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('TailModel',        rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('SocksModel',       rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('NewSocksModel',    rSEnt, wSEnt, StrongEntity(-1), nil, false)

    @NetworkVar('Fly',                  rBool,   wBool,                 false)
    @NetworkVar('DisableTask',          rBool,   wBool,                 false)
    @NetworkVar('UseFlexLerp',          rBool,   wBool,                  true)
    @NetworkVar('FlexLerpMultiplier',   rFloat(0, 10),  wFloat,             1)

    for key, value in pairs PPM2.PonyDataRegistry
        @NetworkVar(value.getFunc, value.read, value.write, value.default)

    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() and IsValid(ent.__PPM2_PonyData\GetOwner()) and StrongEntity(ent.__PPM2_PonyData\GetOwner()) ~= StrongEntity(@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)
            @Reset()
    GenericDataChange: (state) =>
        hook.Run 'PPM2_PonyDataChanges', @ent, @, state
        if state\GetKey() == 'Entity' and IsValid(@GetEntity())
            @SetupEntity(@GetEntity())

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

        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()]

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

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

    ResetScale: =>
        if scale = @GetSizeController()
            scale\ResetScale()

    ModifyScale: =>
        if scale = @GetSizeController()
            scale\ModifyScale()

    Reset: =>
        if scale = @GetSizeController()
            scale\Reset()
        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

        if scale = @GetSizeController()
            scale\PlayerRespawn()

        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

        if scale = @GetSizeController()
            scale\PlayerDeath()

        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()
        @GetWeightController()\SlowUpdate() if @GetWeightController()
        if scale = @GetSizeController()
            scale\SlowUpdate()

    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
    GetSizeController: =>
        return @scaleController if not @isValid
        if not @scaleController or @modelCached ~= @modelScale
            @modelCached = @modelCached or @ent\GetModel()
            @modelScale = @modelCached
            cls = PPM2.GetSizeController(@modelCached)
            if @scaleController and cls == @scaleController.__class
                @scaleController.ent = @ent
                PPM2.DebugPrint('Skipping size controller recreation for ', @ent, ' as part of ', @)
                return @scaleController
            @scaleController\Remove() if @scaleController
            @scaleController = cls(@)
        @scaleController.ent = @ent
        return @scaleController
    GetScaleController: => @GetSizeController()
    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)
        @GetBodygroupController()\Remove() if @GetBodygroupController()
        @GetSizeController()\Remove() if @GetSizeController()
        @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 'StrongEntityLinkUpdates', 'PPM2.NetworkedObjectCheck', =>
		if @__PPM2_PonyData
            @__PPM2_PonyData\SetEntity(@GetEntity())
            @__PPM2_PonyData\SetupEntity(@)
else
    hook.Add 'PlayerJoinTeam', 'PPM2.TeamWaypoint', (ply, new) ->
        ply.__ppm2_modified_jump = false
    hook.Add 'OnPlayerChangedTeam', 'PPM2.TeamWaypoint', (ply, old, new) ->
        ply.__ppm2_modified_jump = false

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

wUInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteUInt(arg, size)

wInt = (def = 0, size = 8) ->
    return (arg = def) -> net.WriteInt(arg, size)

rUInt = (size = 8, min = 0, max = 255) ->
    return -> math.Clamp(net.ReadUInt(size), min, max)

rInt = (size = 8, min = -128, max = 127) ->
    return -> math.Clamp(net.ReadInt(size), min, max)

rFloat = (min = 0, max = 255) ->
    return -> math.Clamp(net.ReadFloat(), min, max)

wFloat = net.WriteFloat
rSEnt = net.ReadStrongEntity
wSEnt = net.WriteStrongEntity
rBool = net.ReadBool
wBool = net.WriteBool
rColor = net.ReadColor
wColor = net.WriteColor
rString = net.ReadString
wString = net.WriteString

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

    @Setup()
    @NetworkVar('Entity',           rSEnt, wSEnt, StrongEntity(-1), ((newValue) => IsValid(@GetOwner()) and StrongEntity(@GetOwner()\EntIndex()) or newValue))
    @NetworkVar('UpperManeModel',   rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('LowerManeModel',   rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('TailModel',        rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('SocksModel',       rSEnt, wSEnt, StrongEntity(-1), nil, false)
    @NetworkVar('NewSocksModel',    rSEnt, wSEnt, StrongEntity(-1), nil, false)

    @NetworkVar('Fly',                  rBool,   wBool,                 false)
    @NetworkVar('DisableTask',          rBool,   wBool,                 false)
    @NetworkVar('UseFlexLerp',          rBool,   wBool,                  true)
    @NetworkVar('FlexLerpMultiplier',   rFloat(0, 10),  wFloat,             1)

    for key, value in pairs PPM2.PonyDataRegistry
        @NetworkVar(value.getFunc, value.read, value.write, value.default)

    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() and IsValid(ent.__PPM2_PonyData\GetOwner()) and StrongEntity(ent.__PPM2_PonyData\GetOwner()) ~= StrongEntity(@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)
            @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() == '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()]

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

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

    ResetScale: =>
        if scale = @GetSizeController()
            scale\ResetScale()

    ModifyScale: =>
        if scale = @GetSizeController()
            scale\ModifyScale()

    Reset: =>
        if scale = @GetSizeController()
            scale\Reset()
        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

        if scale = @GetSizeController()
            scale\PlayerRespawn()

        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

        if scale = @GetSizeController()
            scale\PlayerDeath()

        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()
        @GetWeightController()\SlowUpdate() if @GetWeightController()
        if scale = @GetSizeController()
            scale\SlowUpdate()

    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
    GetSizeController: =>
        return @scaleController if not @isValid
        if not @scaleController or @modelCached ~= @modelScale
            @modelCached = @modelCached or @ent\GetModel()
            @modelScale = @modelCached
            cls = PPM2.GetSizeController(@modelCached)
            if @scaleController and cls == @scaleController.__class
                @scaleController.ent = @ent
                PPM2.DebugPrint('Skipping size controller recreation for ', @ent, ' as part of ', @)
                return @scaleController
            @scaleController\Remove() if @scaleController
            @scaleController = cls(@)
        @scaleController.ent = @ent
        return @scaleController
    GetScaleController: => @GetSizeController()
    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)
        @GetBodygroupController()\Remove() if @GetBodygroupController()
        @GetSizeController()\Remove() if @GetSizeController()
        @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 'StrongEntityLinkUpdates', 'PPM2.NetworkedObjectCheck', =>
		if @__PPM2_PonyData
            @__PPM2_PonyData\SetEntity(@GetEntity())
            @__PPM2_PonyData\SetupEntity(@)
else
    hook.Add 'PlayerJoinTeam', 'PPM2.TeamWaypoint', (ply, new) ->
        ply.__ppm2_modified_jump = false
    hook.Add 'OnPlayerChangedTeam', 'PPM2.TeamWaypoint', (ply, old, new) ->
        ply.__ppm2_modified_jump = false

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


gay: add function gaywords

gay: add function gaywords

Former-commit-id: bbe21fe1c37535bc23a9515891b6b7c66eaa4531 [formerly 8959294ea91c9c59da69c31f7b4d5662f10e3a14]
Former-commit-id: 3ef31bb51d837df3bff8a36158619dd3b78ef3d9

FABULOUS_COLORS = {4, 7, 9, 10, 12, 13, 6}

i = 0

gayify = (s) ->
  if s == "" return ""
  s\gsub "[%z\1-\127\194-\244][\128-\191]*", (c) ->
    ret = string.format("\03%02d%s", FABULOUS_COLORS[i + 1], c)
    i = (i + 1) % #FABULOUS_COLORS
    return ret

gaywordify = (s) ->
  if s == "" return ""
  ivar2.util.translateWords s, (c) ->
    ret = string.format("\03%02d%s", FABULOUS_COLORS[i + 1], c)
    i = (i + 1) % #FABULOUS_COLORS
    return ret

PRIVMSG:
  '^%pgay (.+)$': (source, destination, arg) =>
      say gayify arg
  '^%pgaywords (.+)$': (source, destination, arg) =>
      say gaywordify arg

FABULOUS_COLORS = {4, 7, 9, 10, 12, 13, 6}

i = 0

gayify = (s) ->
  if s == "" return ""
  s\gsub "[%z\1-\127\194-\244][\128-\191]*", (c) ->
    ret = string.format("\03%02d%s", FABULOUS_COLORS[i + 1], c)
    i = (i + 1) % #FABULOUS_COLORS
    return ret

PRIVMSG:
  '^%pgay (.+)$': (source, destination, arg) =>
      say gayify arg