2.1.1 • Published 5 years ago

@articulatesydney/gmservice v2.1.1

Weekly downloads
16
License
-
Repository
-
Last release
5 years ago

GM-SERVICE

About

本JS库目的在于管理游戏列表 主要特性有 1. 提供常用的异步数据支持 2. 封装常用的展示游戏列表的方式 3. 基于rxjs 非常extendable

GMService基于rxjs,数据交互操作全部为异步实现。 获取数据的接口的使用方式基本上都是

instance.someapi.subscribe(data => {
    //...
})

完整API列表

如何使用

安装

npm install --save @articulatesydney/gmservice

或者直接在html中加入

 <script src="....bundle.js"></script>

测试

npm run test

Build

npm run build

使用

  1. 设置参数并初始化gmservice 得到gmservice的instance
var instance = new gmservice({
    games: GAMELISR,
    cache: 10000,
    launchGame: function (game, { resolve }) {
        // ... resolve(xxxx)
    }
})
  1. 从instance提供的api中获取数据

    获取provider

instance.providers.subscribe(providers => {
    console.log(providers);
})

获取完整游戏列表

instance.list(games => {
    console.log(games);
})

Tips

  1. 请避免使用fat arrow functions(transform函数以及process函数除外)
  2. 在gallery中使用transform.order的时候, GALLERY_FORMAT.PROVIDERS_OF_CATEGORIES模式中将会先调用category的order方法;对于每个category内部的providers的顺序会由groups order决定(example请见测试用例以及example lobby.html)

Configuration

gmservice内部默认认为传入的游戏列表为标准格式, 可以在games中直接传入标准格式的游戏列表;也可以使用rawProcess函数进行

标准格式包含以下property

  1. cn_name: string
  2. en_name: string
  3. image: string
  4. code: string
  5. provider: string
  6. category:string
  7. types: object - 这个设计是为了实现一个游戏可以同时有多个类型的需求
{
    slot: true,
    jackpot: true,
    asian: true
}
  1. extra: object - 用于记录额外的数据:可能在自定义的搜索中使用

配置列表

var instance = new gmservice({
    
    /**
    * 1. games
    * - 如果网页版游戏列表与h5有出入, 具体如何加载合适的游戏列表需要使用者自己处理
    * 
    * format: 
    *  - array of games OR
    *  - function
    *    主要用于异步加载游戏列表:在load完成后调用resolve
    *  
    */
    games: gamelist,
    games: (resolve) => {
        fetch('/gamelist')
        .then(d => d.json())
        .then(games => {
            resolve(games)
        })

        /*
        games: (resolve) => resolve(gamelist)
        与
        games: gamelist
        等价
        */
    }

    /**
     * 2. providers 
     * - 在游戏列表上筛选特定平台的游戏
     * - 主要用于在cache了完整的游戏列表的环境中 只向玩家显示特定平台的相关游戏数据
     * 
     * **如果游戏列表中本来就没有给定平台的游戏 也不会造成多余的显示
     * 
     * providers format
     * - array of provider names
     * - function
     * 
    */
    providers: ['PNG', 'MG']
    providers: (resolve) => {
        fetch('/valid_providers')
        .then(d => d.json())
        .then(providers => resolve(providers));
    },

    /**
     * 3. categories
     * - 在游戏列表上筛选特定类型的游戏
     * - 主要用于在cache了完整的游戏列表的环境中 只向玩家显示特定类型的相关游戏数据
     * 
     * **如果游戏列表中本来就没有给定平台的游戏 也不会造成多余的显示
     * 
     * categories format
     * - array of provider names
     * - function
     * 
    */
    categories: ['slots', 'live']
    categories: (resolve) => {
        fetch('/valid_categories')
        .then(d => d.json())
        .then(categories => resolve(categories));
    },

    /**
     * 4. filter - function
     * - 在游戏列表上自定义筛选游戏
     * - 主要用于根据特殊条件过滤游戏: 比如说 不希望玩家看到 rtp超过95%的游戏;
     * 或者不希望玩家看到PNG的带有slot字样的游戏
     * 
     * filter format
     * - function
     * 
    */
    filter: (game) => {
        return game.extra.rtp < 0.95;
        // return game.provider !== 'PNG' || game.en_name.search('slots') < 0
    }

    /**
     * 5. rawProcess - function
     * - 用于对游戏列表进行预处理 以转换至标准格式
     * 
    */
    rawProcess: function (g) {
        return Object.assign(g, {
            code: g.game_code,
            cn_name: g.name,
            en_name: g.english_name,
            types: (g.game_type || '').split('_').reduce((prev, t) => ({ ...prev, [t]: true }), {}),
            extra: {}
        })
    },

    /**
     * 6. injectUpdates - function
     * 
     * function injectUpdates({gamelist, cache_updated_at, cache_expire_at}, resolveFn): void
     * 
     * - 检查游戏列表是否有更新:如果有的话将新的数据纳入系统
     * - 新的数据不会被rawProcess处理(因为可能还有updated/removed games的情况,injectUpdates用来处理一些非常灵活的情形)
     * - 如果同时还开启了cache 新的数据以及当前的timestamp将被写入cache;做为下次调用时的cache_updated_at
     * 
     * - 一个特别的使用例子:在lobby上使用时 推荐cache设置为999999999: 即使有cache也会检查更新,而且更新的timestamp会被写在cache中
     * 
    */
    injectUpdates: ({gamelist, cache_updated_at, cache_expire_at}, resolve) => {

        fetch(`/new_games?updated_at=${cache_updated_at}`)
        .then(d => d.json())
        .then((newgames = []) => {
            resolve([...gamelist, newgames]);
        })
    },


    /**
     * 7. cache - 
     * - 保存处理完成之后的游戏列表
     * - 如果有合法的cached游戏列表存在的话 将不会使用games参数传入的游戏列表或者function
     * - 网页版与h5版的游戏列表将分别cache
     * - 
     * 
     * format:
     *  - number - 浏览器中使用localStorage: 在浏览器中保存
     *  - string: 'session' - 浏览器中使用 sessionStorage: 本次tab session中cache游戏列表
     * 
    */
    cache: 1000 * 60 * 60 * 24 * 7, // 1 week
    cache: 'session',


    /**
     * 8. recommends: 
     * - 接受的数据为一个array:将index=0的数据视为全局推荐游戏列表
     *  index=1的数据视为对应agent的推荐游戏列表
     * - 为了解决inconsistancy 推荐游戏列表实际上提供一些标准 还是会从gamelist中去匹配合适的数据;每个object只需要但必须要提供provider,category以及code
     * 
     * format:
     *  - array of gamelist arrays [gamelist1, gamelist2]
     *  - function
     * 
    */
   recommends: [general_recommends, operator_recommends],
   recommends: (resolve) => {
       fetch('/some-recommends?playerid=111')
       .then(d => d.json())
       .then(data => resolve(data))
   },

   /**
     * 9. recommendProcess - function
     * - 类似于 rawProcess: 将推荐游戏列表的数据处理成标准状态
     * 
    */
   recommendProcess: function (g) {
        return ({
            category: g.category,
            provider: g.provider,
            code: g.code,
            extra: {
                priority: g.priority,
            }
        });
    },

    /**
     * 10. recommendStrategy - function
     * - 接受一个第一个元素为全局推荐游戏列表、第二个元素为agent推荐游戏列表的array为参数
     * - 返回一个推荐游戏array
     * - strategy将在 recommendProcess之前调用:数据尚未被处理
     * - 默认stategy为 general.concat(operator); 主要使用方式包括 将operator推荐游戏前置 或者将某一类型的推荐游戏前置
     * 
    */
   recommendStrategy: ([general, operator]) => 
        general.filter(g => g.provider == 'PNG').concat(operator)

    /**
     * 11. launchGame - function
     * - 打开游戏请求的处理函数
     * - 在instance上使用getLaunchUrl时触发
     *   resolve and launchGame get success status and data
     *   reject and  launchGame get error status and data
     * 
    **/
    launchGame: (game, { resolve, reject }) => {
        const {provider, code} = game;
        fetch(`/open-game?provider=${provider}&code=${code}`)
        .then(response => {
            if (provider == 'PNG') resolve('....' + response);
            else resolve(response);
        })
        .catch(error => {
            reject(error);
        })
    },

    /**
     * 12. getBalance - function
     * - 获取平台余额的处理函数
     * - 在instance上使用 getBalance 时触发
     *   resolve and getBalance get success status and data
     *   reject and  getBalance get error status and data
     * 
    **/
    getBalance: (provider, { resolve, reject }) => {
        fetch(`/get——balance?provider=${provider}`)
        .then(balance => {
            resolve(balance);
        })
        .catch(error => {
            reject(error);
        })
    },

    /**
     * 12. transform
     * 
     * format: {order: fn, rename:fn}
     * 
     * - transform主要是用于
     * - transform会在gmservice即将输出的时候执行,不会对内容数据产生影响:因此可以配合config使用、即时更新输出数据
     * - 主要的使用方式: 更改名字、改变order,以及在切换语言的时候即时更新所有受影响的部分
     * 
     * - order: function (groups, providers, categories, types):void
     *   会在原有的object上操作 所有需要使用 .sort/.splice 之类的function
     * 
     * - rename: function(keyword, type)
     *   rename如果没有明确返回值 则使用原本的value
     *   rename type分为了三类 provider, category 以及type
    **/

   /*
    const cateOrder = {
                'sports': 1,
                'live': 2,
                'slots': 3,
                'sports': 4
    }
   */

    transform: {
        rename: function(key, type) {
            if (type == TYPES.GAMETYPE) return ({
                        roulette: "百家乐",
                        slots: '老虎机',
                        probability: '概率',
                        tablegame: '桌游',
                        videopoker: '扑克游戏',
                        'video-bingo': "宾果"
                    })[key];
            if (type == TYPES.CATEGORY) return ({
                slots: '电子游戏'
            })[key];
        },
        order: function(groups, providers, categories, types) {
            if (categories) categories.sort((cateA, cateB) => {
                return cateOrder[cateA] - cateOrder[cateB]
            })
        }
    },



})

API

  • group: provider + category 比如说 PNG slots MG live是三个不同的group

  • active group 任何时候都会存在一个provider+category的组合作为active group;默认值为 {provider: null, category: null}. gamelist, types, recommend等数据都会受当前active group影响

    • 同时存在provider与category
    • 只有provider - 显示provider下所有category的数据
    • 只有category - 显示category下所有provider的数据
    • 都没有 - 显示全局数据
  • filters gameStream emit的是active group中的游戏列表, filterStream emit的是gameStream 的数据经过筛选后的结果. 筛选的filter有三种

    • keyword: 游戏名称
    • type: 游戏类型
    • limitTo: 最大显示游戏数
  • stream stream即rxjs中的observable: 在api中主要操作方式为 .subscribe ; 所有以stream结尾命名的api都会多次emit数据:只需要subscribe一次; 如果只想获取一次数据可以使用 take(1) 比如 instance.typeStream.take(1).subscribe(types => {/*....*/})

#API功能示例
1recommends(groupObject)根据provider + category获取推荐游戏列表instance.recommends({provider: 'PNG', category: 'live'}).subscribe(games => {/* .... */})
2recommendStream当前active group的推荐游戏列表instance.recommendStream.subscribe(games => {/* .... */})
3list完整游戏列表instance.list(games => {/* do something */})
4setGroup设置active的groupinstance.setGroup({provider:'PNG', category: 'slots'}}) instance.setGroup({provider:'Betsoft'})
5setFilter(filters, reset) (limitTo默认为20) reset如果为true 则会将filters先reset再加上传入的filters; 如果为false,则只会更新传入的filter,未传入的部分不变更新Filterinstance.setFilter({keyword: 'diamond'})instance.setFilter({ type:'slot' })instance.setFilter({ limitTo: 50})instance.setFilter({}, true)
6loadMore(howmany?) 默认额外加载20个游戏加载更多游戏(更新filter)instance.loadMore() instance.loadMore(30)
7gameStream当前active group对应游戏列表instance.gameStream .subscribe(games => {/* do something */})
8filteredStreamfilter之后的当前active group对应游戏列表instance.filteredStream .subscribe(games => {/* do something */})
9gameGroupStream (相同于gameStream+typeStream)active group的数据
10typeStream (注意!只有当active group同时设置了provider与category, typeStream才会有数据)当前active group对应类型列表instance.typeStream .subscribe(types => {/* do something */})
11activeGroup当前active的groupinstance.activeGroup .subscribe(group => {/* do something */})
12getLaunchUrl打开游戏api.getLaunchUrl({ provider: 'PNG', code: 1000 }) .subscribe(data => { data.status == 'success' && window.open(data.data) || alert(data.data)
13getBalance获取平台余额api.getBalance('PNG') .subscribe(data => { data.status == 'success' && alert('balance=' + data.data) || alert(data.data)
14providers (会被transform影响)平台列表instance.providers .subscribe(providers => {/* do something */})
15balances 会在getBalance请求完成时更新(注意: 进入或退出loading的状态不会造成balances的emit)平台余额Mapinstance.balances .subscribe(balances => {/* do something */})
16categories (会被transform影响)类型列表instance.categories .subscribe(categories => {/* do something */})
17groups (会被transform影响)group列表instance.groups .subscribe(groups => {/* do something */})
18indicators 两个subscribable的indicator: moreToLoad (boolean) , loading (array of targets in loading state)当前数据状态的指示indicators.moreToLoad .subscribe(moreToLoad => moreToLoad && show(loadMoreButton); }) indicators.loading .subscribe(states => { app.ready = states.length === 0 })
19gallery(会被transform影响)按照一定格式获取全部或者部分数据instance.gallery({}, GALLERY_FORMAT.PROVIDERS_OF_CATEGORIES).subscribe(games => {...}) instance.gallery({provider:'PNG'}).subscribe(games => {...})
20config重新设置rename/order函数; 操作之后各个stream将会重新计算并emit 因此可以支持语言切换api.config({transform: {rename: fn}})

Loading State 列表

  • LOADING_GAMELIST
  • LOADING_PROVIDERS
  • LOADING_CATEGORIES
  • LOADING_RECOMMENDS

GALLERY_FORMAT 列表

  • CATEGORIES_OF_PROVIDERS
  • PROVIDERS_OF_CATEGORIES

TYPES 列表

  • PROVIDER
  • GAMETYPE
  • CATEGORY
2.1.1

5 years ago

2.1.0

6 years ago

2.0.0

6 years ago

1.0.0

6 years ago