Ace of Spades "Anti-grefer Bot"

The original, free Ace of Spades game powered by the Voxlap engine. Known as “Classic,” 0.75, 0.76, and all 0.x versions. Created by Ben Aksoy.
18 posts Page 1 of 2 First unread post
Lee Revell
Deuce
Posts: 8
Joined: Sun Sep 20, 2015 3:44 pm


As you know there are grefers in Ace of Spades. Blue_Surprised2 Green_Surprised2 But I have had an idea!!! Blue_Wink1 Green_Wink1 What if we made "Anti-grefer Bots", which constantly patrol the land, searching for grefers. When it finds them it reports and kills them. Yes, Ace of Spades is not in development anymore and Build and Shoot has revived it, but what if we done it through the servers. I have done some research and have found the code for the Deuce Bots in bot servers;
Code: Select all
    [code]# BASIC BOTS
    # fakes a connection and partially replicates player behavior
    #
    # pathfinding was stripped out since it is unfinished and depended
    # on the C++ navigation module
    #
    # requires adding the 'local' attribute to server.py's ServerConnection
    #
    # *** 201,206 ****
    # --- 201,207 ----
    #       last_block = None
    #       map_data = None
    #       last_position_update = None
    # +     local = False
    #       
    #       def __init__(self, *arg, **kw):
    #           BaseConnection.__init__(self, *arg, **kw)
    # *** 211,216 ****
    # --- 212,219 ----
    #           self.rapids = SlidingWindow(RAPID_WINDOW_ENTRIES)
    #       
    #       def on_connect(self):
    # +         if self.local:
    # +             return
    #           if self.peer.eventData != self.protocol.version:
    #               self.disconnect(ERROR_WRONG_VERSION)
    #               return
    #
    # bots should stare at you and pull the pin on a grenade when you get too close
    # /addbot [amount] [green|blue]
    # /toggleai

    from math import cos, sin
    from enet import Address
    from twisted.internet.reactor import seconds, callLater
    from pyspades.protocol import BaseConnection
    from pyspades.server import input_data, weapon_input, set_tool, grenade_packet
    from pyspades.world import Grenade
    from pyspades.common import Vertex3
    from pyspades.collision import vector_collision
    from pyspades.constants import *
    from commands import admin, add, name, get_team

    LOGIC_FPS = 4.0

    @name('addbot')
    def add_bot(connection, amount = None, team = None):
        protocol = connection.protocol
        if team:
            bot_team = get_team(connection, team)
        blue, green = protocol.blue_team, protocol.green_team
        amount = int(amount or 1)
        for i in xrange(amount):
            if not team:
                bot_team = blue if blue.count() < green.count() else green
            bot = protocol.add_bot(bot_team)
            if not bot:
                return "Added %s bot(s)" % i
        return "Added %s bot(s)" % amount

    @name('toggleai')
    def toggle_ai(connection):
        protocol = connection.protocol
        protocol.ai_enabled = not protocol.ai_enabled
        if not protocol.ai_enabled:
            for bot in protocol.bots:
                bot.flush_input()
        state = 'enabled' if protocol.ai_enabled else 'disabled'
        protocol.send_chat('AI %s!' % state)
        protocol.irc_say('* %s %s AI' % (connection.name, state))

    add(add_bot)
    add(toggle_ai)

    class LocalPeer:
        #address = Address(None, 0)
        address = Address('255.255.255.255', 0)
        roundTripTime = 0.0
       
        def send(self, *arg, **kw):
            pass
       
        def reset(self):
            pass

    def apply_script(protocol, connection, config):
        class BotProtocol(protocol):
            bots = None
            ai_enabled = True
           
            def add_bot(self, team):
                if len(self.connections) + len(self.bots) >= 32:
                    return None
                bot = self.connection_class(self, None)
                bot.join_game(team)
                self.bots.append(bot)
                return bot
           
            def on_world_update(self):
                if self.bots and self.ai_enabled:
                    do_logic = self.loop_count % int(UPDATE_FPS / LOGIC_FPS) == 0
                    for bot in self.bots:
                        if do_logic:
                            bot.think()
                        bot.update()
                protocol.on_world_update(self)
           
            def on_map_change(self, map):
                self.bots = []
                protocol.on_map_change(self, map)
           
            def on_map_leave(self):
                for bot in self.bots[:]:
                    bot.disconnect()
                self.bots = None
                protocol.on_map_leave(self)
       
        class BotConnection(connection):
            aim = None
            aim_at = None
            input = None
            acquire_targets = True
            grenade_call = None
           
            _turn_speed = None
            _turn_vector = None
            def _get_turn_speed(self):
                return self._turn_speed
            def _set_turn_speed(self, value):
                self._turn_speed = value
                self._turn_vector = Vertex3(cos(value), sin(value), 0.0)
            turn_speed = property(_get_turn_speed, _set_turn_speed)
           
            def __init__(self, protocol, peer):
                if peer is not None:
                    return connection.__init__(self, protocol, peer)
                self.local = True
                connection.__init__(self, protocol, LocalPeer())
                self.on_connect()
                #~ self.saved_loaders = None
                self._send_connection_data()
                self.send_map()
               
                self.aim = Vertex3()
                self.target_orientation = Vertex3()
                self.turn_speed = 0.15 # rads per tick
                self.input = set()
           
            def join_game(self, team):
                self.name = 'Deuce%s' % str(self.player_id)
                self.team = team
                self.set_weapon(RIFLE_WEAPON, True)
                self.protocol.players[(self.name, self.player_id)] = self
                self.on_login(self.name)
                self.spawn()
           
            def disconnect(self, data = 0):
                if not self.local:
                    return connection.disconnect(self)
                if self.disconnected:
                    return
                self.protocol.bots.remove(self)
                self.disconnected = True
                self.on_disconnect()
           
            def think(self):
                obj = self.world_object
                pos = obj.position
               
                # find nearby foes
                if self.acquire_targets:
                    for player in self.team.other.get_players():
                        if vector_collision(pos, player.world_object.position, 32.0):
                            self.aim_at = player
                            break
               
                # replicate player functionality
                if self.protocol.game_mode == CTF_MODE:
                    other_flag = self.team.other.flag
                    if vector_collision(pos, self.team.base):
                        if other_flag.player is self:
                            self.capture_flag()
                        self.check_refill()
                    if not other_flag.player and vector_collision(pos, other_flag):
                        self.take_flag()
           
            def update(self):
                obj = self.world_object
                pos = obj.position
                ori = obj.orientation
               
                if self.aim_at and self.aim_at.world_object:
                    aim_at_pos = self.aim_at.world_object.position
                    self.aim.set_vector(aim_at_pos)
                    self.aim -= pos
                    distance_to_aim = self.aim.normalize() # don't move this line
                    # look at the target if it's within sight
                    if obj.can_see(*aim_at_pos.get()):
                        self.target_orientation.set_vector(self.aim)
                    # creeper behavior
                    if self.acquire_targets:
                        if distance_to_aim < 16.0 and self.grenade_call is None:
                            self.grenade_call = callLater(3.0, self.throw_grenade,
                                0.0)
               
                # orientate towards target
                diff = ori - self.target_orientation
                diff.z = 0.0
                diff = diff.length_sqr()
                if diff > 0.001:
                    p_dot = ori.perp_dot(self.target_orientation)
                    if p_dot > 0.0:
                        ori.rotate(self._turn_vector)
                    else:
                        ori.unrotate(self._turn_vector)
                    new_p_dot = ori.perp_dot(self.target_orientation)
                    if new_p_dot * p_dot < 0.0:
                        ori.set_vector(self.target_orientation)
                else:
                    ori.set_vector(self.target_orientation)
               
                if self.grenade_call:
                    self.input.add('primary_fire')
               
                obj.set_orientation(*ori.get())
                self.flush_input()
           
            def flush_input(self):
                input = self.input
                world_object = self.world_object
                z_vel = world_object.velocity.z
                if 'jump' in input and not (z_vel >= 0.0 and z_vel < 0.017):
                    input.discard('jump')
                input_changed = not (
                    ('up' in input) == world_object.up and
                    ('down' in input) == world_object.down and
                    ('left' in input) == world_object.left and
                    ('right' in input) == world_object.right and
                    ('jump' in input) == world_object.jump and
                    ('crouch' in input) == world_object.crouch and
                    ('sneak' in input) == world_object.sneak and
                    ('sprint' in input) == world_object.sprint)
                if input_changed:
                    if not self.freeze_animation:
                        world_object.set_walk('up' in input, 'down' in input,
                            'left' in input, 'right' in input)
                        world_object.set_animation('jump' in input, 'crouch' in input,
                            'sneak' in input, 'sprint' in input)
                    if (not self.filter_visibility_data and
                        not self.filter_animation_data):
                        input_data.player_id = self.player_id
                        input_data.up = world_object.up
                        input_data.down = world_object.down
                        input_data.left = world_object.left
                        input_data.right = world_object.right
                        input_data.jump = world_object.jump
                        input_data.crouch = world_object.crouch
                        input_data.sneak = world_object.sneak
                        input_data.sprint = world_object.sprint
                        self.protocol.send_contained(input_data)
                primary = 'primary_fire' in input
                secondary = 'secondary_fire' in input
                shoot_changed = not (
                    primary == world_object.primary_fire and
                    secondary == world_object.secondary_fire)
                if shoot_changed:
                    if primary != world_object.primary_fire:
                        if self.tool == WEAPON_TOOL:
                            self.weapon_object.set_shoot(primary)
                        if self.tool == WEAPON_TOOL or self.tool == SPADE_TOOL:
                            self.on_shoot_set(primary)
                    world_object.primary_fire = primary
                    world_object.secondary_fire = secondary
                    if not self.filter_visibility_data:
                        weapon_input.player_id = self.player_id
                        weapon_input.primary = primary
                        weapon_input.secondary = secondary
                        self.protocol.send_contained(weapon_input)
                input.clear()
           
            def set_tool(self, tool):
                if self.on_tool_set_attempt(tool) == False:
                    return
                self.tool = tool
                if self.tool == WEAPON_TOOL:
                    self.on_shoot_set(self.world_object.fire)
                    self.weapon_object.set_shoot(self.world_object.fire)
                self.on_tool_changed(self.tool)
                if self.filter_visibility_data:
                    return
                set_tool.player_id = self.player_id
                set_tool.value = self.tool
                self.protocol.send_contained(set_tool)
           
            def throw_grenade(self, time_left):
                self.grenade_call = None
                if not self.hp or not self.grenades:
                    return
                self.grenades -= 1
                if self.on_grenade(time_left) == False:
                    return
                obj = self.world_object
                grenade = self.protocol.world.create_object(Grenade, time_left,
                    obj.position, None, obj.orientation, self.grenade_exploded)
                grenade.team = self.team
                self.on_grenade_thrown(grenade)
                if self.filter_visibility_data:
                    return
                grenade_packet.player_id = self.player_id
                grenade_packet.value = time_left
                grenade_packet.position = grenade.position.get()
                grenade_packet.velocity = grenade.velocity.get()
                self.protocol.send_contained(grenade_packet)
           
            def on_spawn(self, pos):
                if not self.local:
                    return connection.on_spawn(self, pos)
                self.world_object.set_orientation(1.0, 0.0, 0.0)
                self.aim.set_vector(self.world_object.orientation)
                self.target_orientation.set_vector(self.aim)
                self.set_tool(GRENADE_TOOL)
                self.aim_at = None
                self.acquire_targets = True
                connection.on_spawn(self, pos)
           
            def on_connect(self):
                if self.local:
                    return connection.on_connect(self)
                max_players = min(32, self.protocol.max_players)
                protocol = self.protocol
                if len(protocol.connections) + len(protocol.bots) > max_players:
                    protocol.bots[-1].disconnect()
                connection.on_connect(self)
           
            def on_disconnect(self):
                for bot in self.protocol.bots:
                    if bot.aim_at is self:
                        bot.aim_at = None
                connection.on_disconnect(self)
           
            def on_kill(self, killer, type, grenade):
                if self.grenade_call is not None:
                    self.grenade_call.cancel()
                    self.grenade_call = None
                for bot in self.protocol.bots:
                    if bot.aim_at is self:
                        bot.aim_at = None
                return connection.on_kill(self, killer, type, grenade)
           
            def _send_connection_data(self):
                if self.local:
                    if self.player_id is None:
                        self.player_id = self.protocol.player_ids.pop()
                    return
                connection._send_connection_data(self)
           
            def send_map(self, data = None):
                if self.local:
                    self.on_join()
                    return
                connection.send_map(self, data)
           
            def timer_received(self, value):
                if self.local:
                    return
                connection.timer_received(self, value)
           
            def send_loader(self, loader, ack = False, byte = 0):
                if self.local:
                    return
                return connection.send_loader(self, loader, ack, byte)
       
        return BotProtocol, BotConnection
Like in the server below;

[aos]aos://151723184:32895:0.75[/aos]

There are problems though, because the Deuce Bots are stationary and only throw grenades. But with some help I think we can all make this possible. Or we could redesign the servers themselves, so that they see and know everything that is on the server, so that there isn't any physical bot, which would be better and easier... Please Comment, E-mail or Private Message me if you want to be part of this. By the way, I am terrible with Python Script. Nothing a bit of reading cant solve. Blue_Happy3 Green_Happy3 Thank you! Blue_Happy1 Green_Happy1

Lee Revell
Attachments
basicbots.py
(14.37 KiB) Downloaded 283 times
Last edited by Lee Revell on Tue Sep 22, 2015 3:58 pm, edited 1 time in total.
longbyte1
Deuced Up
Posts: 336
Joined: Sun Jul 21, 2013 7:27 pm


We can't make the bots move until one of us learns a bit about pathfinding.
LeCom


longbyte1 wrote:
We can't make the bots move until one of us learns a bit about pathfinding.
Actually one can apply some simple tricks to make them not get stuck that often (like, moving a few voxels to the side when hitting a wall which even results in an actual bug2 pathfinder or directly following players). However, I don't see the point in having bots do that if the server "sees" everything and can kill players.
bloodfox
Post Demon
Post Demon
Posts: 2206
Joined: Mon Oct 21, 2013 4:32 pm


longbyte1 wrote:
We can't make the bots move until one of us learns a bit about pathfinding.
pathfinding.... *shudder*
Marisa Kirisame
Deuced Up
Posts: 152
Joined: Sat Sep 21, 2013 10:52 pm


Lee Revell wrote:
What if we made "Anti-grefer Bots", which constantly patrol the land, searching for grefers. When it finds them it reports and kills them.
Or you could just kickban the bastards from the server end. Saves having to waste time making killable player bots which is a lot harder than the already difficult grief detection AI you want to write. Sorry, I mean the AI you want us to write.

And one more thing
Lee Revell wrote:
Yes, Ace of Spades is not in development anymore and Build and Shoot has revived it
You mean froze it, and then it suffered from freezerburn?
Captain_Pi
Artist
Artist
Posts: 323
Joined: Thu Dec 12, 2013 1:20 pm


/votekick
Marisa Kirisame
Deuced Up
Posts: 152
Joined: Sat Sep 21, 2013 10:52 pm


Captain_Pi wrote:
/votekick
* Votekick for TheGrandmaster has ended. Player kicked
* TheGrandmaster banned for 30 minutes: HACKER
* TheGrandmaster disconnected
longbyte1
Deuced Up
Posts: 336
Joined: Sun Jul 21, 2013 7:27 pm


/votenecro
...no votes?
/thread
TheGrandmaster
Former Pre-BnS Team
Former Pre-BnS Team
Posts: 124
Joined: Tue Oct 23, 2012 2:54 pm


Marisa Kirisame wrote:
Captain_Pi wrote:
/votekick
* Votekick for TheGrandmaster has ended. Player kicked
* TheGrandmaster banned for 30 minutes: HACKER
* TheGrandmaster disconnected
I approve of this.

...I think.


--------------
(and since I've always hated off-topic posts, even though the thread is somewhat dead:)

I think it may make more sense to get something working for automatically detecting griefing players first, forgetting about the faux-player part, and then figure out how to then effectively implement that inside of a bot, because there are alot of design questions on this topic.

If you are able to figure out some reasonable parameters for what defines griefing - what they have to be doing, what statistics you might need about them (and timeframes) etc, then you have a chance at having an acceptable amount of false-positives.. but I think, as with most things, it's much easier for a human to say 'well, yes, of course that player is griefing' than trying to define the act solely with numbers and figures.. humans are good at semi-abstract concepts.. bots, not so much.

There are many potential design questions regarding placing the (hopefully working by this stage) griefer-detecting functionality into a bot. The current workflow I would assume to be something like this:
  • Bot traverses map
  • Bot sees player
  • Bot looks up player's history and calculates probability of griefing
  • Bot sees player removing user-placed blocks, makes decision
Firstly, would you use pre-programmed (aka, waypointed/scripted) routes for the bot, a random path, or a smart path?
If waypointed, that is going to require mappers' support, and we all know that isn't going to happen.
If a random path, that bot is gonna end up in Timbuktu at some point or another and probably won't be in areas of use that much.
If a smart path - what would the parameters for 'smart' bot placement be? What data would it need to collate in order to choose where to go? The only thing I can think of is to track the placement user-placed blocks, form clusters (hopefully showing where user-placed blocks are concentrated on the map), then map waypoints dynamically that would go between/around/through each cluster. Still, not as simple as it sounds.

Secondly, vision - how far can the bot see, and at what angle? If you want player-like realism, you would have to give them a cone of view, a viewport, and therefore not only would you have to control the bot's path, but you'd also have to figure out where the bot should be looking along the way, and that's an extra layer of complexity. The alternative is 360 degree "vision" that just detects in a radius around the bot, which means it would just do the grief checking on players that are within a certain distance of the bot. The viewport can be made simpler by just having some assumptions - always looking forward, or always looking towards the next waypoint, for example, but then it might miss detecting someone.

Thirdly- hnm, I'm pretty sure that I had a third point I was going to make but I can't remember it.. and I don't think I need to, even.

tldr; it's actually fairly complicated to make an intelligent bot for a purpose, and humans would do a better job at it - as said earlier in this thread, /votekick or some method to more easily flag these players by humans would probably be better.. alas, we do not have the game's source code to do something about that much.

(not that I didn't recommend and suggest this a long time ago... Uploaded Jul. 18th, 2011)
Image
There have already been scripts to add more to a votekick too - some stats about blocks destroyed etc. that is pretty useful info.
bloodfox
Post Demon
Post Demon
Posts: 2206
Joined: Mon Oct 21, 2013 4:32 pm


hi.
u r alive.
LeCom


Your screenshot looks very interesting - you probably can't imagine how awesome that would be in AoS today, considering the votekick participation ratio. But why post such a huge thing about something that probably nobody gives a fuck about.
bloodfox
Post Demon
Post Demon
Posts: 2206
Joined: Mon Oct 21, 2013 4:32 pm


eh, I like the idea none the less and I would like to see it in there. So the GUI for the votekick system would be a plugin?
longbyte1
Deuced Up
Posts: 336
Joined: Sun Jul 21, 2013 7:27 pm


I guess you can write that as a plugin for learn_more's modloader. Unless of course everyone thinks that his modloader is awful.

I've thought about pathfinding for a while but have not read up on the various 3D pathfinding algorithms, so what I have in my head right now is basically a node map that is built/updated periodically. Unless anyone wants me to talk about it in further detail, I will stop there.
bloodfox
Post Demon
Post Demon
Posts: 2206
Joined: Mon Oct 21, 2013 4:32 pm


Yeah, but how are you able to load a skin like that on a server without any installation required?
longbyte1
Deuced Up
Posts: 336
Joined: Sun Jul 21, 2013 7:27 pm


bloodfox wrote:
Yeah, but how are you able to load a skin like that on a server without any installation required?
Skin?

Well if you want to load arbitrary code you can always just find a buffer overflow exploit in the net code. It's just a matter of time.

It's a bit harder though when you don't have IDA.
18 posts Page 1 of 2 First unread post
Return to “Ace of Spades 0.x Discussion”

Who is online

Users browsing this forum: No registered users and 25 guests